home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Copyright ΓòÉΓòÉΓòÉ
-
- 26-Feb-1996
-
- BULLET is Copyright 1992-96, and is owned by the author, Cornel Huth,
- and is protected by United States copyright laws and international
- treaty provisions. License restrictions apply.
-
-
- ΓòÉΓòÉΓòÉ 2. License Agreement ΓòÉΓòÉΓòÉ
-
- Before using this software, BULLET/2 (or simply, BULLET) you must agree to the
- following:
-
- 1. A BULLET/2 license grants you the right to use the BULLET/2 library code
- on a royalty-free basis according to the terms of this License Agreement.
-
- 2. You are not permitted to operate more than one copy of this software
- package at one time per license. For example, if you have ten
- programmers that have access to the BULLET/2 package at the same time,
- you are required to have ten BULLET/2 licenses.
-
- 3. There is no restriction on the number of users you may support, and no
- restriction on the number of different end-user programs you may
- distribute that use BULLET/2. You may allow any number of simultaneous
- users to use your end-user program.
-
- 4. The dynamic link library, BULLET2.DLL, may be distributed with your
- end-user program. No other BULLET product may be distributed without
- permission (for example, you may not distribute Bullet's import library,
- BULLET2I.LIB).
-
- 5. You are not permitted to distribute non-executable code containing
- BULLET/2 code. This means that may not redistribute BULLET/2 with your
- program if your program can be used by other programmers to develop
- executable code. BULLET/2 must be part of an end-user product only.
- This means that you cannot provide an overlay or other such external code
- containing BULLET/2 code if that code is to be used as a programming
- library for other programmers, from which the other programmers can
- create programs. If you require distributing a non-end-user package
- containing BULLET/2, you must obtain written permission from the author.
- This limitation pertains to the distribution of BULLET/2 library code.
- You may, however, develop and distribute your programmer package (ie
- non-end-user) as you wish, but you may not distribute BULLET/2 library
- code with that package without written permission. For example, you may
- develop class libraries that use the BULLET/2 library code, and
- distribute those tools that you have written, but you may not include
- BULLET/2 library code, or BULLET/2 activation methods, in that package.
- The programmer using your package would need a BULLET/2 license to make
- use of your package.
-
- 6. The static link library, BULLET2.LIB, may not be distributed except in
- executable form as a component in your executable program (EXE).
- BULLET2.LIB may not be placed into a DLL. BULLET2.LIB, if part of your
- license/Option, may only be linked directly to your end-user program.
- BULLET2.LIB is available with Option C licenses only.
-
- 7. Shareware use is limited to 28 days, and for the sole purpose of
- evaluating the software. The BULLET/2 library code may not be
- distributed in any form without a registered BULLET/2 license. A
- BULLET/2 license is obtained only with purchase of a BULLET package,
- purchased from an authorized BULLET distributor (see Order Information).
-
- 8. A BULLET license is specific to the option level purchased. License
- holders with a lower Option may not use any higher level Option code.
- For example, if you find another product using the Option C DLL, and you
- have an Option A license, you are not permitted to use the Option C DLL
- in your development, nor may you distribute any code that is not part of
- your Option level.
-
- 9. Your end-user program using the BULLET/2 DLL is required to be a
- copyrighted work, and must contain a valid copyright notice in the form,
- 'Program-name Copyright (C)Year Your-Name', or similar. No notice of
- BULLET's copyright need be further specified in your program (in other
- words, you don't need to mention BULLET, but you may if you wish). This
- applies only if you distribute the BULLET/2 DLL with your product.
- Programs that are linked using the BULLET2.LIB static link library need
- not display a copyright notice. In other words, if you must distribute
- to the Public Domain, where no copyright is desired for your program, you
- must link using the static link library, and not the DLL.
-
- 10. BULLET/2 is owned by the author, Cornel Huth, and is protected by United
- States copyright laws and international treaty provisions. You are not
- permitted to make copies of this software except for archival purposes.
-
- 11. You may not rent or lease BULLET/2. You may not transfer this license
- without the written permission of the author. If this software is an
- update or upgrade, you may not sell or give away previous versions.
-
- 12. You may not reverse engineer, decompile, or disassemble this software if
- the intent or result is to alter the software.
-
- 13. There are no expressed or implied warranties with this software.
-
- 14. All liabilities in the use of this software rest with the user.
-
- 15. U.S. Government Restricted Rights. This software is provided with
- restricted rights. Use, duplication, or disclosure by the Government is
- subject to restrictions as set forth in subparagraph (c)(1)(ii) of the
- Rights in Technical Data and Computer Software clause at 52.227-7013. The
- software is owned by Cornel Huth/6402 Ingram Rd/San Antonio Texas
- 78238/USA. This agreement is governed by the laws of the Great State of
- Texas, the United States of America, and all other countries of Earth.
-
- Any questions concerning this License Agreement should be directed to me at
- any of the addresses listed in Product Support.
-
- Note: Failure to comply with any part of this License Agreement immediately
- terminates any and all licenses that you may have to use BULLET/2.
-
-
- ΓòÉΓòÉΓòÉ 3. Installation ΓòÉΓòÉΓòÉ
-
- Installation instructions are located in the README text file included with
- your package.
-
-
- ΓòÉΓòÉΓòÉ 4. Product Support ΓòÉΓòÉΓòÉ
-
- Support for licensed users is available at my BBS, The 40th Floor, 7 days a
- week. Hours are 5pm to 9am, Central Time (USA, -0600 GMT (-0500
- April-October). Weekend BBS hours are 24 hours (5pm Friday to 9am Monday).
- Hours other than above are voice. A fax can be sent during the times the BBS
- is operating. Limited technical support for licensed users is also available
- through e-mail (see e-mail address below). Please use the Bug Report Form when
- reporting possible bugs.
-
- Note: Technical support is reserved for licensed users. Those evaluating
- Bullet are supported for the Bullet installation only. Complete support is
- available once you register.
-
- BBS: +1(210)684-8065 (times listed above), N-8-1.
-
- The latest in-version (2.x) release of BULLET/2 is always available for free
- download by registered users ($10- by mail). Bugs, if re-creatable, are fixed
- within 24 hours. Also available at the BBS are other shareware
- try-before-you-buy products by me, such as the DOS32/Win/NT versions of Bullet;
- the Ruckus/DOS soundcard toolkit; the linear programming optimizer LP;
- interesting 'Specs', and more still. Current shareware versions are available
- at several sites, with the FTP site listed below being the primary distribution
- point.
-
- My E-mail address:
-
- cornel@crl.com
- (FTP) ftp.crl.com /users/co/cornel
- (WWW) ftp://ftp.crl.com/users/co/cornel
-
-
- ΓòÉΓòÉΓòÉ 4.1. Bug Report Form ΓòÉΓòÉΓòÉ
-
- When requesting support for possible bug(s) you must follow these steps (always
- use the current version of the software, and read the product Bulletin/Errata
- if available):
-
- 1. Include a complete problem description.
- 2. Include sample source of the problem, if necessary (99 lines or less).
- 3. Include necessary data files, include files, etc. (no 3rd-party
- DLL/LIBs).
- 4. Include step-by-step procedure to follow in order to recreate the
- problem.
-
- Once done, ZIP it up and upload to the appropriate conference (or to the
- e-mail address, in uuencoded form). There, leave a summary and the filename
- of the ZIP uploaded (or e-mail message). Tech support does not start on
- reports unless all steps above are completed. All files must be included,
- including the Bullet DLL you are using, header files, etc., so that a compile
- can be done in an empty directory without changes (no paths added, etc.).
-
- The goal is for you to minimize the possibility that the bug is in fact your
- doing. The best way to ensure this is to remove all extraneous source, and to
- isolate the problem to a specific sequence of operations. It must be done in
- 99 lines or less. If the problem is recreated, it is fixed within 24 hours.
-
- Note: For non-bug reports, compose your query and post to the conference (or
- e-mail). Off-line composing is recommended. See the Main Board Bulletin for
- exact details (if applicable).
-
- Unregistered users are not supported except for basic install information.
-
-
- ΓòÉΓòÉΓòÉ 5. Ordering Information ΓòÉΓòÉΓòÉ
-
- To order Bullet by check, bank check, money-order, or cash, use form
- !ORDER.FRM. For credit card order, use form !ORDER.CC.
-
- Payment Options
-
- Γûá Check, money-order, cash
-
- Funds must be in US Dollars and must be drawn on a US bank (complete with bank
- routing numbers). If sending currency, use Registered AirMail. Personal
- checks require 10 working days to clear. Most major foreign banks have branch
- banks in the US and this can be used for US Dollars/US bank requirements.
- Direct wire-transfer is not available. Send payment and order form !ORDER.FRM
- to:
-
- Cornel Huth
- 6402 Ingram Rd
- San Antonio, Texas 78238-3915
- USA
-
- Γûá Credit card
-
- For credit card orders (Visa, MC, Amex, Discover), call (800)242-4775 (outside
- US, call 1(713)524-6394), or fax to 1(713)524-6398, or via CompuServe e-mail to
- 71355,470 (or internet e-mail to 71355.470@compuserve.com), requesting 'Bullet
- for OS/2' (item# 11185). Include the completed credit card order form when
- using e-mail or fax.
-
- Phone orders are taken during business hours, which for PsL is weekdays from
- 7AM to 6PM, USA Central Time (7AM to 12:30PM on Friday). Fax or CompuServe
- orders are accepted at any time.
-
- Credit card ordering can be done through the mail by writing to PsL/PO Box
- 35705/Houston TX 77235-5705/USA.
-
- Note: This is for ordering Bullet; do not use these numbers for any other
- purpose! Be sure to specify 'Bullet for OS/2' and your license option, as
- shown in the schedule below. Your shipping address should be the same as your
- billing address for fastest processing. Fax or postal orders should use order
- form !ORDER.CC. Please type, or print using large characters. Double-check
- your information to make sure it is correct. Incomplete or inaccuarate order
- information requires that I contact you to request that you re-order.
-
- Order Options
-
- Price for your Bullet license is based on this schedule:
-
- Option A is $ 99 for 2-process/100 open files per process, DLL
- Option B is $198 for 32-process/250 open files per process, DLL
- Option C is $297 for unlimited-process/1024 open files per process, DLL and LIB
-
- The BULLET2.DLL may be distributed with your end-user program. There are no
- run-time fees and there are no royalty fees. Refer to the LICENSE AGREEMENT
- for limitations on distributing BULLET2.DLL with a non-end-user package.
-
- Γûá Option A is for most programmers. It permits two processes to use the Bullet
- DLL at the same time. This is not a user limit, but a process limit. Two
- processes means that you can start two different programs that use BULLET2.DLL,
- and have them both run at the same time, on the same machine. Before starting
- a third program that uses BULLET2.DLL, you must end one or both of the previous
- two. Most programmers should need only 1 Bullet process active at a time.
- There's an extra process should you need it. The total number of open Bullet
- DBF and index files may be up to 100 files per process, total (sum of data and
- index; memo files are not counted against this). Most programmers should not
- need more than 100 files open at the same time. Documentation includes the
- online version, and an ASCII text suitable for printer output. Standard
- AirMail shipping cost is included in the price. For US/Canada Express Mail on
- Option A, add $16 (foreign delivery, add $21). The printed reference manual is
- available with Options B and C only.
-
- Γûá Option B permits 32 processes to use the Bullet DLL at the same time. The
- total number of open Bullet DBF and index files may be up to 250 files per
- process, total (sum of data and index; memo files are not counted against
- this). Documentation includes the online version, an ASCII text suitable for
- printer output, and the printed Reference Manual. US/Canada shipments are made
- using USPS Express Mail. Foreign shipments are made using International
- Express Mail. Express Mail shipping cost is included in the price.
-
- Γûá Option C permits an unlimited number of processes (limited only by the OS
- itself) to use the Bullet DLL at the same time. In addition to the BULLET
- dynamic link library (BULLET2.DLL), Option C provides the BULLET/2 static link
- library (BULLET2.LIB), which may be directly linked into your end-user
- executable program so as to not require BULLET2.DLL. The total number of open
- Bullet DBF and index files may be up to 1024 files per process, total (sum of
- data and index; memo files are not counted against this). Special-order
- versions with more than 1024 files are available. Documentation includes the
- online version, an ASCII text suitable for printer output, and the printed
- Reference Manual. US/Canada shipments are made using USPS Express Mail.
- Foreign shipments are made using International Express Mail. Express Mail
- shipping cost is included in the price.
-
- Delivery Options
-
- Standard AirMail is 3-5 days for US destinations, and 5-14 days for
- international destinations (standard shipping for Option A). Express Mail is
- next-day to most US destinations, and two-day to most Canadian destinations.
- International Express Mail is 3-5 days. Disk media is 3.5-inch, 1.4MB unless
- requested otherwise. Express mailings are shipped with two identical disk
- sets. Options B and C always ship Express Mail or International Express Mail.
-
- Note: All foreign shipments may go through your country's Customs. Each
- package is valued at the total price of the order, less shipping costs, if any,
- unless pre-arranged otherwise. Your Customs Office may delay mail delivery if
- duty is required. Direct download of the registered, licensed version is
- available from the Bullet BBS for the absolute fastest delivery.
-
- Licensed Bullet versions may be download directly from the support BBS. To do
- this: 1) Order Bullet; 2) Call the BBS and tell me (SysOp) that you've ordered
- Bullet; 3) Upon receipt of your order, I'll leave you detailed instructions on
- getting your licensed version via BBS download. Shipment of your package will
- still be made, unless you specify otherwise.
-
- Click here [Γûá] for the check, money-order, cash order form.
-
- Click here [Γûá] for the credit card order from.
-
-
- ΓòÉΓòÉΓòÉ 5.1. Order Form for Check, Money-order, or Cash ΓòÉΓòÉΓòÉ
-
- Printing this section from here may not fit on a single page. Use the file
- !ORDER.FRM instead, or select the 'Services' menu item from here (upper-left),
- and select 'Copy to file' to print this to .\TEXT.TMP. Your e-mail address can
- be used instead of a fax number.
-
- Bullet/2 2.0 for OS/2 Order Form for Check, Money-order, or Cash
- Details on Options A-C are in 'Ordering Information' in the documentation.
-
- Cost Per Number of
- License Licenses Extended
- --------------------------------------------------------------------
- Option A. Single-license, 2 processes/100 files per process DLL
- Includes AirMail. $ 99.00 x =
- ------ ---------
- Standard AirMail, for all> 0.00 |
- For Express Mail, USA/Canada> 16.00 |select one
- For Express Mail, all others> 21.00 | =
- ------------------------------------------------------ ---------
- Option B. Single-license, 32 processes/250 files per process DLL
- Includes Express Mail. $ 198.00 x =
- ------------------------------------------------------ ---------
- Option C. Single-license, unlimited processes/1024 files per process
- DLL and static link library
- Includes Express Mail. $ 297.00 x =
- ------------------------------------------------------ ---------
- For Texas shipping destinations only, add 7.75% =
- ---------
- TOTAL =
- =========
-
- Send TOTAL payment on check (US Dollars/US bank), money-order, or cash to
-
- CORNEL HUTH Always endorse your letter
- 6402 INGRAM RD AirMail
- SAN ANTONIO TX 78238-3915 if you are not in the USA
- USA
-
- PLEASE PRINT - For comments use back and mark here [ ]
-
- Your Name>
- -------------------------------------------------- (required)
-
- Company>
- -------------------------------------------------- (optional)
-
- Address>
- -------------------------------------------------------------
-
-
- -------------------------------------------------------------
-
-
- -------------------------------------------------------------
-
- Telephone Fax Disk
- Number> --------------------- No.> ------------------- Size> --------
-
- Sign> Today's Date>
- ---------------------------------- ---/----/---
-
-
- ΓòÉΓòÉΓòÉ 5.2. Order Form for Credit Card ΓòÉΓòÉΓòÉ
-
- Printing this section from here may not fit on a single page. Use the file
- !ORDER.CC instead, or select the 'Services' menu item from here (upper-left),
- and select 'Copy to file' to print this to .\TEXT.TMP. Your e-mail address can
- be used instead of a fax number.
-
- Bullet/2 2.0 for OS/2 Order Form for Credit Card (PsL Item# 11185)
- Details on Options A-C are in 'Ordering Information' in the documentation.
-
- Cost Per Number of
- License Licenses Extended
- --------------------------------------------------------------------
- Option A. Single-license, 2 processes/100 files per process DLL
- Includes AirMail. $ 99.00 x =
- ------ ---------
- Standard AirMail, for all> 0.00 |
- For Express Mail, USA/Canada> 16.00 |select one
- For Express Mail, all others> 21.00 | =
- ------------------------------------------------------ ---------
- Option B. Single-license, 32 processes/250 files per process DLL
- Includes Express Mail. $ 198.00 x =
- ------------------------------------------------------ ---------
- Option C. Single-license, unlimited processes/1024 files per process
- DLL and static link library
- Includes Express Mail. $ 297.00 x =
- ------------------------------------------------------ ---------
- For Texas shipping destinations only, add 7.75% =
- ---------
- TOTAL =
- =========
-
- Call this in by telephone voice @ 1(800)242-4775 or 1(713)524-6394, or
- fax this to 1(713)524-6398, or Compuserve e-mail to 71355,470, or
- internet e-mail to 71355.470@compuserve.com. For mail address see docs.
-
- PLEASE PRINT - For comments add a separate page and mark here [ ]
-
- Your Name>
- -------------------------------------------------- (required)
-
- Company>
- -------------------------------------------------- (optional)
-
- Address>
- -------------------------------------------------------------
-
-
- -------------------------------------------------------------
-
-
- -------------------------------------------------------------
-
- Telephone Fax Disk
- Number> --------------------- No.> ------------------- Size> --------
-
- Credit
- Card Type> ---------- Number> ----------------------------- Exp> ---/---
-
- Sign> Today's Date>
- ---------------------------------- ---/----/---
-
-
- ΓòÉΓòÉΓòÉ 6. Tutorial ΓòÉΓòÉΓòÉ
-
- This tutorial describes how to set up a basic database. It describes file
- layout and how to use Bullet to enter into and read from the database. It does
- not describe aspects that do not relate to Bullet, such as how to load list
- boxes, create dialogs, etc. This tutorial creates a music CD collection
- database.
-
- The tutorial follows.
-
-
- ΓòÉΓòÉΓòÉ 6.1. Tutorial: Output Requirements ΓòÉΓòÉΓòÉ
-
- The goal of this tutorial is to create a database for a music CD collection.
- The method used is a suggested method; it is by no means the only way to
- develop a database, and only touches the surface of database programming. That
- said...
-
- The most important thing to do is to know what output you want. From that, you
- know what input is needed. For the 'CD database', I've drawn up a list of
- things that I want:
-
- Title, artist, and year. Must have those. Track name (the song name), track
- number, and play time would be essential, too. How about who was playing on
- the track and instruments they used? Put that on your list -- it's not on this
- one.
-
- All these could be entered into a single file as a single database record, but
- that would be a pretty poor design. A single data file may seem to be the
- obvious way, but in the long run, it's not very good at all. This database
- uses two data files. This concept -- multiple data files -- is especially
- useful when the database is more complex. For example, an accounting system
- would employ many physical data files. It could be done in one, sure, but the
- maintenance would be very difficult.
-
- Two tables are used: The Title-Artist-Year table (Title/CTAY), and the
- TrackNumber-TrackName-Time table (Tracks/CTTT). A table is a file composed of
- rows and columns. A row is a record; a column is a field. No two rows may be
- exactly alike (what would be the point?). In addition to the fields listed
- (the Title, Artist, and Year, for instance), each table includes a Code field.
- TrackNumber is included as a field value in the second table since the physical
- ordering of rows cannot be used for this purpose (it could, technically, but it
- is unwise to depend on row order being preserved since there is nothing
- enforcing the physical ordering of records).
-
- The Code field for the Title table (CTAY) is generated by the program and is
- composed of the first 4 characters of the Title, plus the first 4 characters of
- the Artist, plus the year (all four characters of it). This code is also used
- in the Tracks table (CTTT). This serves two purposes: First, it ensures that
- each Track record is unique (with pretty good certainty; without it, it is
- possible that TrackNumber-TrackName-Time alone may occur more than once), and
- second, it can be used as the primary key when being accessed through the
- foreign key in the Title table (see Record Layout Diagrams). By searching CTAY
- (the Title Table) for a desired title, or artist, or even year, and using the
- Code field from that row, you can match it to the Code in CTTT (Track Table) to
- find the tracks that belong to that title row (or artist, or year -- whatever
- the search was based on). To speed up searching, indexes are maintained and
- used for lookups. For a very large CD collection (say, several thousand CD
- titles, and tens of thousands of tracks), the database can still be very
- quickly queried for whatever information you need (and have programmed).
-
-
- ΓòÉΓòÉΓòÉ 6.2. Tutorial: Record Layout ΓòÉΓòÉΓòÉ
-
- The Code field is generated by your program. Other fields are input data. The
- following are the data record layouts of the DBF files (tag fields not shown).
- The time field may be created as a text field with the form "mm:ss", or could
- even be made into a 'seconds' field. This database uses the more direct
- "mm:ss" since that's how it's read off the CD. Note that this information is
- already stored digitally on the CD itself (running time), but that would
- require additional coding and is not relevant to this tutorial (but oh, how
- sweet it could be).
-
- CTAY TABLE
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé CODE Γöé TITLE Γöé ARTIST ΓöéYEAR Γöé
- Γöé Γöé Γöé Γöé Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- MexiConc1993 Mexican Moon Concrete Blonde 1993
- No NCran1994 No Need to Argue Cranberries, The 1994
- MachDeep1972 Machine Head Deep Purple 1972
-
-
- CTTT TABLE
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé CODE ΓöéTRKΓöé TRACKNAME ΓöéTIME Γöé
- Γöé Γöé Γöé Γöé Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- MexiConc1993 01 Jenny I Read 05:18
- MexiConc1993 02 Mexican Moon 05:03
- MexiConc1993 03 Heal It Up 04:21
- MexiConc1993 04 Jonestown 06:06
- MexiConc1993 05 Rain 03:28
- MexiConc1993 06 I Call It Love 05:15
- MexiConc1993 07 Jesus Forgive Me 05:17
- MexiConc1993 08 When You Smile 04:18
- MexiConc1993 09 Close To Home 03:31
- MexiConc1993 10 One Of My Kind 03:55
- MexiConc1993 11 End Of The Line 04:39
- MexiConc1993 12 Blind Ambition 06:10
- MexiConc1993 13 Bajo La Lune 05:07
- No NCran1994 01 Ode To My Family 04:30
- No NCran1994 02 I Can't Be With You 03:07
- No NCran1994 03 Twenty One 03:08
- No NCran1994 04 Zombie 05:06
- No NCran1994 05 Empty 03:26
- No NCran1994 06 Everything I Said 03:53
- No NCran1994 07 The Icicle Melts 02:54
- No NCran1994 08 Disappointment 04:14
- No NCran1994 09 Ridiculous Thoughts 04:31
- No NCran1994 10 Dreaming My Dreams 03:37
- No NCran1994 11 Yeat's Grave 02:59
- No NCran1994 12 Daffodil Lament 06:09
- No NCran1994 13 No Need To Argue 02:56
- MachDeep1972 01 Highway Star 06:05
- MachDeep1972 02 Maybe I'm A Leo 04:51
- MachDeep1972 03 Pictures Of Home 05:03
- MachDeep1972 04 Never Before 03:56
- MachDeep1972 05 Smoke On The Water 05:40
- MachDeep1972 06 Lazy 07:19
- MachDeep1972 07 Space Truckin' 04:31
-
-
- ΓòÉΓòÉΓòÉ 6.3. Tutorial: Index Files Used ΓòÉΓòÉΓòÉ
-
- The index files created for this database are, for CTAY: Title+Artist and
- Artist+Title (the + indicates a compound key made up of more than one field);
- for CTTT: Code+TrackName and TrackName+Code. With these indexes, searching can
- be made on CD title, by Artist, or by TrackName. If searching by year is
- required, an additional index could be used, such as Year+Artist+Title (Title
- being used as part of the key in case an Artist releases more than one title in
- a year).
-
- To search by CD title, a title is used as the key. Even a partial title can be
- used during the actual lookup. For example, 'Mexican'. From this, a search is
- made in the Title+Artist index. The first key starting with 'Mexican' is
- returned. The index can then be traversed to find any other keys also starting
- with this (by getting the next key). Since Bullet has high-level access
- routines (GET_FIRST_XB, for example), where a key search returns the data
- record, once a key is found its data is already in memory (the record is
- typically read directly into a structure variable you have set up). This lets
- you move through the CTAY file getting each record in key order, one at a time
- (one key and record per call to Bullet).
-
- With the data record in memory, the Code field in the record (CTAY table) is
- used as a foreign key into CTTT. A new search needs to be done, this time on
- the Code+TrackName index of the CTTT table. With the search key set to
- 'MexiConc1993' (the foreign key field in CTAY of the record that first matched
- 'Mexican'), the Code+TrackName index for CTTT is searched. The first key
- starting with 'MexiConc1993' is returned, and its data record. In that record
- is the track name, track number, and track time. When done with that record,
- the index can be tranversed in-order (forward, or even backward), and, so long
- as the first 12 characters of the Code+TrackName key match 'MexiConc1993', that
- key's record is used. When the match is no longer true in those first 12
- characters, which in the example data occurs after 13 tracks have been read, it
- means that all matching keys in that index file have been found, and there are
- no others matching the criterion.
-
- The TrackName part of Code+TrackName (the part of the key after the first 12
- characters) is not needed as a match criterion in this case; TrackName is being
- used solely for the purpose of making the key unique to that particular CD
- Title (all Code fields are the same for each trackname on the CD).
-
- A possible additional index file would be a key based on Code, for CTAY (there
- is such an index already for CTTT). This would allow indexed access to the
- CTAY table when looking for the Title of a track name. (In other words, you
- have a list of track names of all CDs in the database, in track name order, and
- want to know the CD Title for each track you see.) Even without an index this
- can be done by using the Code value in CTTT as a sequential search lookup value
- in CTAY (searching for the matching Code value in CTAY). This additional index
- is not used here.
-
-
- ΓòÉΓòÉΓòÉ 6.4. Tutorial: Creating the Data Files ΓòÉΓòÉΓòÉ
-
- Now that the output requirements are known, and the layout of the data records
- of the two data files constructed, and the index access methods designed, the
- database can be created. Bullet provides routines to create the files of the
- database according to specifications that you supply.
-
- The CTAY (Code-Title-Artist-Year) and CTTT (Code-Track-TrackName-Time) tables
- are to be in this format:
-
- CTAY TABLE
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé CODE Γöé TITLE Γöé ARTIST ΓöéYEAR Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- MexiConc1993 Mexican Moon Concrete Blonde 1993 (sample record)
- :
-
- CODE character data, 12 bytes
- TITLE character data, 32 bytes
- ARTIST character data, 35 bytes
- YEAR character data, 4 bytes
-
-
- CTTT TABLE
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé CODE ΓöéTRKΓöé TRACKNAME ΓöéTIME Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- MexiConc1993 01 Jenny I Read 05:18 (sample record)
- :
-
- CODE character data, 12 bytes
- TRK character data, 2 bytes
- TRACKNAME character data, 40 bytes
- TIME character data, 5 bytes
-
- The fields are sized primarily on expected need. Since each DBF record has a
- 1-byte tag field, the sum of the above record layouts, including the tag byte,
- is aligned to an even 4-byte size. An admirable goal, but not a primary design
- consideration. CTAY is 84 bytes per record; CTTT is 60 bytes per record.
-
- Data is not entered during the create process. That comes later. The create
- is only to make the physical files. The following is the Bullet code required
- to build the two data files. The index files are covered later.
-
- typedef struct _CTAY {
- CHAR tag;
- CHAR code[12];
- CHAR title[32];
- CHAR artist[35];
- CHAR year[4];
- } CTAY; // (total CTAY record length is 84 bytes)
- CTAY ctayRec;
-
- CHAR ctayName[] = "CTAY.DBF";
- ULONG ctayID=0; // handle of CTAY file
- FIELDDESCTYPE ctayFieldList[4]; // 4 fields used by the record
- memset(ctayFieldList,0,sizeof(ctayFieldList)); // init unused bytes to 0
-
- typedef struct _CTTT {
- CHAR tag;
- CHAR code[12];
- CHAR trk[2];
- CHAR trackName[40];
- CHAR time[5];
- } CTTT; // (total CTTT record length is 60 bytes)
- CTTT ctttRec;
-
- CHAR ctttName[] = "CTTT.DBF";
- ULONG ctttID=0;
- FIELDDESCTYPE ctttFieldList[4];
- memset(ctttFieldList,0,sizeof(ctttFieldList));
-
- // field descriptor info for CTAY
-
- strcpy(ctayFieldList[0].fieldName, "CODE");
- ctayFieldList[0].fieldType = 'C';
- ctayFieldList[0].fieldLen = 12;
- ctayFieldList[0].fieldDC = 0;
-
- strcpy(ctayFieldList[1].fieldName, "TITLE");
- ctayFieldList[1].fieldType = 'C';
- ctayFieldList[1].fieldLen = 32;
- ctayFieldList[1].fieldDC = 0;
-
- strcpy(ctayFieldList[2].fieldName, "ARTIST");
- ctayFieldList[2].fieldType = 'C';
- ctayFieldList[2].fieldLen = 35;
- ctayFieldList[2].fieldDC = 0;
-
- strcpy(ctayFieldList[3].fieldName, "YEAR");
- ctayFieldList[3].fieldType = 'C';
- ctayFieldList[3].fieldLen = 4;
- ctayFieldList[3].fieldDC = 0;
-
- // field descriptor info for CTTT
-
- strcpy(ctttFieldList[0].fieldName, "CODE");
- ctttFieldList[0].fieldType = 'C';
- ctttFieldList[0].fieldLen = 12;
- ctttFieldList[0].fieldDC = 0;
-
- strcpy(ctttFieldList[1].fieldName, "TRACK");
- ctttFieldList[1].fieldType = 'C';
- ctttFieldList[1].fieldLen = 2;
- ctttFieldList[1].fieldDC = 0;
-
- strcpy(ctttFieldList[2].fieldName, "TRACKNAME");
- ctttFieldList[2].fieldType = 'C';
- ctttFieldList[2].fieldLen = 40;
- ctttFieldList[2].fieldDC = 0;
-
- strcpy(ctttFieldList[3].fieldName, "TIME");
- ctttFieldList[3].fieldType = 'C';
- ctttFieldList[3].fieldLen = 5;
- ctttFieldList[3].fieldDC = 0;
-
- // create the CTAY file
-
- CDP.func = CREATE_DATA_XB;
- CDP.filenamePtr = ctayName;
- CDP.noFields = 4;
- CDP.fieldListPtr = ctayFieldList;
- CDP.fileID = 3;
- rez = BULLET(&CDP);
- if (rez) {
- printf("Failed CTAY create. Err: %d\n",rez);
- return(rez);
- }
-
- // create the CTTT file
-
- CDP.func = CREATE_DATA_XB;
- CDP.filenamePtr = ctttName;
- CDP.noFields = 4;
- CDP.fieldListPtr = ctttFieldList;
- CDP.fileID = 3;
- rez = BULLET(&CDP);
- if (rez) {
- printf("Failed CTTT create. Err: %d\n",rez);
- return(rez);
- }
-
-
- ΓòÉΓòÉΓòÉ 6.5. Tutorial: Opening the Data Files ΓòÉΓòÉΓòÉ
-
- Before using a data file it must be opened. Any DBF file can be opened,
- whether created by Bullet or another program. Before creating indexes for the
- data files created here, the data files must be open. The handles of the open
- data files are used in the index file create process.
-
-
- // open CTAY and store handle to ctayID
-
- OP.func = OPEN_DATA_XB;
- OP.filenamePtr = ctayData;
- OP.asMode = READWRITE | DENYNONE;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed CTAY file open. Err: %d\n",rez);
- return(rez);
- }
- ctayID = OP.handle;
-
- // open CTTT and store handle to ctttID
-
- OP.func = OPEN_DATA_XB;
- OP.filenamePtr = ctttData;
- OP.asMode = READWRITE | DENYNONE;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed CTTT file open. Err: %d\n",rez);
- return(rez);
- }
- ctttID = OP.handle;
-
-
- ΓòÉΓòÉΓòÉ 6.6. Tutorial: Creating the Index Files ΓòÉΓòÉΓòÉ
-
- Two pairs of index files are used. For CTAY, Title+Artist and Artist+Title;
- for CTTT, Code+TrackName and TrackName+Code. These are expected to be unique,
- and so the index files are specified to not accept duplicate keys. Also,
- rather than using the full field sizes for key expressions, substrings are
- specified. Long keys affect performance, both in index file size and general
- access speed (smaller is usually better), and using the entire field data does
- little to differentiate the key (for the uniqueness requirement). If
- DUPS_ALLOWED were specified, even shorter keys could be used, though duplicates
- generally are not desirable. If there comes a record whose generated key is
- not unique (i.e., it already belongs to another already-added record), either
- modify the data entered so that its key is different, if possible, or redo your
- index files to allow duplicates. You could also play if 'safe' and permit
- duplicates keys to occur from the outset, knowing that not many duplicates will
- occur, where if they do, you can continue and let Bullet managed the duplicate
- key with an enumerator. If many duplicates of keys are possible, re-evaluate
- your key expression to make it more unique. This is done by using a compound
- key with more field components. Still, the fewer components in a key, and the
- shorter, the better the key is.
-
- Index file creation:
-
- CHAR xTitleName[] = "TITLE.IX3";
- CHAR xTitleExp[] = "SUBSTR(TITLE,1,8)+SUBSTR(ARTIST,1,8)";
- CHAT xTitleBuffer[16]; // key buffer for later use
- // note that if DUPS_ALLOWED, the key buffer must
- // provide room for the enumerator (2 bytes more)
-
- CHAR xArtistName[] = "ARTIST.IX3";
- CHAR xArtistExp[] = "SUBSTR(ARTIST,1,8)+SUBSTR(TITLE,1,8)";
- CHAT xArtistBuffer[16];
-
- CHAR xCodeName[] = "CODETRK.IX3";
- CHAR xCodeExp[] = "CODE+SUBSTR(TRACKNAME,1,12)";
- CHAT xCodeBuffer[24];
-
- CHAR xTrackName[] = "TRKNAME.IX3";
- CHAR xTrackExp[] = "SUBSTR(TRACKNAME,1,12)+CODE";
- CHAT xTrackBuffer[24];
-
- // create two index files for CTAY
-
- CIP.func = CREATE_INDEX_XB;
- CIP.filenamePtr = xTitleName;
- CIP.keyExpPtr = xTitleExp;
- CIP.xbLink = ctayID; // the handle of the open CTAY file
- CIP.sortFunction = NLS_SORT; // sort key by NLS for proper mixed-case order
- CIP.codePage = 0; // use OS-default code page
- CIP.countryCode = 0; // use OS-default country code
- CIP.collatePtr = NULL; // no need for a special collate table
- CIP.nodeSize = 512; // 512-byte node size (or 1024, 2048 bytes)
- rez = BULLET(&CIP);
- if (rez) {
- printf("Failed Title index create. Err: %d\n",rez);
- return(rez);
- }
-
- CIP.filenamePtr = xArtistName;
- CIP.keyExpPtr = xArtistExp; // other values still valid from above
- rez = BULLET(&CIP);
- if (rez) {
- printf("Failed Artist index create. Err: %d\n",rez);
- return(rez);
- }
-
- // create two index files for CTTT
-
- CIP.func = CREATE_INDEX_XB;
- CIP.filenamePtr = xCodeName;
- CIP.keyExpPtr = xCodeExp;
- CIP.xbLink = ctttID; // as above...
- CIP.sortFunction = NLS_SORT;
- CIP.codePage = 0;
- CIP.countryCode = 0;
- CIP.collatePtr = NULL;
- CIP.nodeSize = 512;
- rez = BULLET(&CIP);
- if (rez) {
- printf("Failed Code index create. Err: %d\n",rez);
- return(rez);
- }
-
- CIP.filenamePtr = xTrackName;
- CIP.keyExpPtr = xTrackExp; // other values still valid from above
- rez = BULLET(&CIP);
- if (rez) {
- printf("Failed TrackName index create. Err: %d\n",rez);
- return(rez);
- }
-
-
- ΓòÉΓòÉΓòÉ 6.7. Tutorial: Opening the Index Files ΓòÉΓòÉΓòÉ
-
- Before using an index file it must be opened. Only Bullet index files can be
- opened.
-
- // open Title index and store handle to xTitleID
-
- OP.func = OPEN_INDEX_XB;
- OP.filenamePtr = xTitleName;
- OP.asMode = READWRITE | DENYNONE;
- OP.xbLink = ctayID; // note xbLink
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed Title index open. Err: %d\n",rez);
- return(rez);
- }
- xTitleID = OP.handle;
-
- // open Artist index and store handle to artistID
-
- OP.func = OPEN_INDEX_XB;
- OP.filenamePtr = xArtistName;
- OP.asMode = READWRITE | DENYNONE;
- OP.xbLink = ctayID;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed Artist index open. Err: %d\n",rez);
- return(rez);
- }
- xArtistID = OP.handle;
-
- // open Code index and store handle to xCodeID
-
- OP.func = OPEN_INDEX_XB;
- OP.filenamePtr = xCodeName;
- OP.asMode = READWRITE | DENYNONE;
- OP.xbLink = ctttID;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed Code index open. Err: %d\n",rez);
- return(rez);
- }
- xCodeID = OP.handle;
-
- // open TrackName index and store handle to xTrackNameID
-
- OP.func = OPEN_INDEX_XB;
- OP.filenamePtr = xTrackName;
- OP.asMode = READWRITE | DENYNONE;
- OP.xbLink = ctttID;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed TrackName index open. Err: %d\n",rez);
- return(rez);
- }
- xTrackNameID = OP.handle;
-
-
- ΓòÉΓòÉΓòÉ 6.8. Tutorial: Inserting CD Title Data ΓòÉΓòÉΓòÉ
-
- The files have been created, and are open. Data may now inserted into the
- database. The term 'inserted' is used in contrast to 'added' since Bullet
- 'adds' data only, while it 'inserts' data and key information together (and
- especially, the key is inserted, in order). The CD data entry would typically
- be done via the keyboard. This tutorial assumes that this has already been
- done, and that the data items are in the following structure variables:
- ctayRec for CD title data; ctttRec for track data.
-
- First, the CD title record is added to CTAY with a key inserted into the Title
- index and another key into Artist index. Data entered by the user consists of
- CD title, artist, and year. The Code field is generated by the program code,
- shown below.
-
- // ctayRec has CD title, artist, and year -- generate Code field data
-
- strncpy(ctayRec.code ,ctayRec.title,4); // first 4 of title
- strncpy(ctayRec.code+4,ctayRec.artist,4); // first 4 of artist
- strncpy(ctayRec.code+8,ctayRec.year,4); // and 4 of year make the Code field
-
- // insert CD title data (one data record added, two index files inserted into)
-
- AP[0].func = INSERT_XB;
- AP[0].handle = xTitleID; // handle of Title index
- AP[0].recNo = 0; // required
- AP[0].recPtr = &ctayRec; // the CD title record
- AP[0].keyPtr = xTitleBuffer; // key buffer
- AP[0].nextPtr = &AP[1]; // since two index files, point to next
- AP[1].handle = xArtistID: // handle of Artist index
- AP[1].recNo = 0x80000000; // required
- AP[1].recPtr = &ctayRec; // the CD title record
- AP[1].keyPtr = xArtistBuffer; // key buffer
- AP[1].nextPtr = NULL;
-
- rez = BULLET(&AP[0]);
- if (rez) { // rez is not error, but pack index of error
- rc = AP[abs(rez)-1].stat;
- printf("Insert failed, pack: %d, err: %d\n",rez,rc);
- return(rc);
- }
-
- A CD title has been entered. That's the first part of the data entry. Next,
- each track on that CD needs to be entered. It needs the Code field data
- generated above, along with the general track data (track name, etc.).
-
-
- ΓòÉΓòÉΓòÉ 6.9. Tutorial: Inserting CD Track Data ΓòÉΓòÉΓòÉ
-
- During the CD title insert, a Code field value was generated. That field value
- is used for each of the track entries inserted into database for this CD. The
- actual track data has already been placed in the ctttRec structure variable (by
- the main program code, not shown here), and only the Code field of ctttRec
- needs to be set. Once done, the track record is inserted, this time into the
- CTTT data file and the Code and Track index files.
-
- //
- // this code section is repeated for each track on the CD
- //
-
- // copy Code field data from CTAY
-
- strncpy(ctttRec.code,ctayRec.code,12); // use Title's Code for each Track entry
-
- // insert track data (one data record added, two index files inserted into)
-
- AP[0].func = INSERT_XB;
- AP[0].handle = xCodeID; // handle of Code index
- AP[0].recNo = 0; // required
- AP[0].recPtr = &ctttRec; // the track record
- AP[0].keyPtr = xCodeBuffer; // key buffer
- AP[0].nextPtr = &AP[1]; // since two index files, point to next
- AP[1].handle = xTrackID: // handle of TrackName index
- AP[1].recNo = 0x80000000; // required
- AP[1].recPtr = &ctttRec; // the track record
- AP[1].keyPtr = xTrackBuffer; // key buffer
- AP[1].nextPtr = NULL;
-
- rez = BULLET(&AP[0]);
- if (rez) { // rez is not error, but pack index of error
- rc = AP[abs(rez)-1].stat;
- printf("Insert failed, pack: %d, err: %d\n",rez,rc);
- return(rc);
- }
-
- // and repeat for each track on the CD
-
- The database now contains a complete entry for the one CD. This process is
- repeated for each CD that is to entered into the database. This is the data
- entry process. Data retrieval is covered next.
-
-
- ΓòÉΓòÉΓòÉ 6.10. Tutorial: Retrieving Data ΓòÉΓòÉΓòÉ
-
- The database can be accessed in several different ways. For indexed access,
- any index file can be used. Alternatively, direct access without an index
- could also be used. Generally, since there's no guarantee that the physical
- order of rows in the database is in any order at all, index access is usually
- desired. Possible retrievals are by:
-
- 1. CD title, in alphabetical order, and, from information from the CD title,
- all tracks on the CD. The tracks are read into an array and sorted by track
- number. Alternatively, a separate index could be used to order track entries
- by TrackNo+TrackName(+etc.), but since there are so few tracks per CD
- (generally less than 20), it's much more efficient to simply get all tracks for
- a CD title into an array, and then sort that array for presentation.
- Otherwise, an index file would need to be maintained on a permanent basis, and
- for something that can easily be done at run-time (and then discarded).
-
- 2. Artist, in alpha order. Access is similar to CD title, above, since it uses
- the same data file (CTAY). As above, tracks for each artist's title can then
- be retrieved. The only difference between this and the CD title access is that
- this lists CDs by artist, rather than by title.
-
- 3. Track name, in alphabetical order. This lists all tracks by track name, in
- order, with different tracks intermixed with CD titles. In other words, if
- three CDs had the track called "In the Summer Time", then each of those tracks
- would appear together, one after the other. There is no index available in
- this database to access the CD on which the track name appears, so you cannot
- go from trackname alone to CD title (by index). However, since the Code field
- is available, and is common to each data file, the Code field value of a track
- record can be used to sequentially search the Title records for a match. This
- is a non-indexed search. Access is slower than if an index were available, but
- unless you created indexes for all possible search methods, some sequential
- (read: slow) searching may be required. For smaller files (less than 10,000
- records), or where only a few such searches occur, this may be perfectly
- acceptable.
-
- An index on Code exists (for CTTT, but not CTAY), and you may find a use for
- this other than for use as a foreign key lookup. Also, you may create ad hoc
- index files whenever you need to, and these can be deleted when no longer
- required. Since Bullet can create an index for even a large file in just a few
- seconds, this is a viable option; the few seconds needed to create an index may
- be many times repaid in the time saved in sequential searches on a non-indexed
- access.
-
- The first retrieval mentioned is shown next.
-
- // for each CD title in the database, display all its tracks
-
- // ctayRec already defined
- CTTT cttyRecs[59]; // store each Track record for later sorting by track#
-
- // This would be repeated for each CD title (only the first is shown).
- // Note: For the next CD title, GET_NEXT_XB would be used. If necessary,
- // the key just accessed could be stored (preserved), then laster reaccessed
- // using GET_EQUAL_XB again, and then immediately followed with a GET_NEXT_XB.
- // This way, you can stop processing completely, and restart up where you left
- // off.
-
- // files are locked as required (e.g., full-lock, shared)
-
- AP.func = GET_FIRST_XB;
- AP.handle = xTitleID;
- AP.recPtr = &ctayRec;
- AP.keyPtr = xTitleBuffer;
- rez = BULLET(&AP);
- while (rez==0) {
- printf("Title: %s Artist: %s Year: %s\n",
- ctayRec.title,
- ctayRec.artist,
- ctayRec.year);
-
- // get each track belonging to this CD title (same Code values)
-
- int trk=0; // counter
- memset(xCodeBuffer,0,sizeof(xCodeBuffer)); // clear it to 0
- strncpy(xCodeBuffer,ctayRec.code,12); // copy Code to search-for buffer
-
- AP.func = GET_EQUAL_XB;
- AP.handle = xCodeID;
- AP.recPtr = &ctttRecs[trk];
- AP.keyPtr = xCodeBuffer; // find Code
- rez = BULLET(&AP);
-
- // rather than print each when gotten, collect them, sort, then display
- // if rez==0 here then Code matched exactly, no strncmp needed, otherwise,
- // there's no match on the first 12 characters of Code
-
- AP.func = GET_NEXT_XB; // continue getting while same Code
- while (rez==0) {
- trk++; // limit to array size...
- AP.recPtr = &ctttRecs[trk]; // read into next array record
- rez = BULLET(&AP);
- if (rez==0) {
-
- // if code field in CTTT no longer the same as CTAY, then have them all
-
- if (strncmp(ctttRecs[trk].code,ctayRec.code,12)!=0) {
- rez = 1;
- break; // different Code fields
- }
- }
- }
-
- // if rez==1 or at end of file then no error, sort and display --
- // trk at this point is number of tracks that matched Code (1-based)
- // DoSortOf... routine sorts the ctttRecs array with trk count of elements
-
- if ((rez==1) | (rez==EXB_END_OF_FILE)) {
- DoSortOfTheTrackRecordsByTrackNumber(&ctttRecs[0],trk);
-
- // print each track record, now sorted by track number
-
- for (i=0;i < trk;i++) {
- printf("Trk: %s Name: %s Time: %s\n",
- ctttRecs[i].trk,
- ctttRecs[i].trackName,
- ctttRecs[i].time);
- }
- else {
- printf("failed, rez: %d\n",rez);
- return(rez);
- }
- }
-
-
- ΓòÉΓòÉΓòÉ 6.11. Tutorial: Preparing to End Your Program ΓòÉΓòÉΓòÉ
-
- It's proper programming practice to perform an orderly shutdown of Bullet when
- ending your program. This also extends to files, too, where even if you are
- not ending your program, but are finished using a file, close that file so that
- you release the resources allocated for it.
-
- In the event that an abnormal termination occurs, and you are unable to release
- outstanding locks and close all files, you may choose to call EXIT_XB and allow
- that to close files (the OS releases locks when the file is closed). If you
- are unable to call even EXIT_XB, there is still another method that gets
- Bullet's attention. During Bullet initialization, the EXIT_XB routine is
- registered with the OS so that whenever your program terminates (or is
- terminated), EXIT_XB is called automatically. This ensures that headers are
- properly written in all but the most severe of abnormal terminations (abend),
- such as a lockup.
-
- // all outstanding locks should have been released immediately after use
-
- // close each file that was opened
-
- HP.func = CLOSE_DATA_XB
- HP.handle = xTitleID;
- if (HP.handle) rez = BULLET(&HP);
- if (rez) printf("close failed, err: %d\n",rez);
-
- HP.handle = xArtistID;
- if (HP.handle) rez = BULLET(&HP);
- if (rez) printf("close failed, err: %d\n",rez);
-
- HP.handle = xCodeID;
- if (HP.handle) rez = BULLET(&HP);
- if (rez) printf("close failed, err: %d\n",rez);
-
- HP.handle = xTrackID;
- if (HP.handle) rez = BULLET(&HP);
- if (rez) printf("close failed, err: %d\n",rez);
-
- EP.func = EXIT_XB;
- rez = BULLET(&EP);
- if (rez) printf("exit failed, err: %d\n",rez);
-
- // Bullet is now deinitialized, and must be INIT_XB'ed before further use
-
-
- ΓòÉΓòÉΓòÉ 7. History of Changes ΓòÉΓòÉΓòÉ
-
- Changes Made
-
- 2.050 26-Feb-96:
-
- 1. Shareware refresh released.
-
- 2.044 31-Jan-96:
-
- 1. Fixed DUPS_ALLOWED bug at INSERT_XB where error EXB_TOP_OF_FILE or error
- EXB_TOO_MANY_DUPLICATES would be returned.
-
- 2.043 3-Jan-96:
-
- 1. Added atomic key access for NEXT_KEY, PREV_KEY, GET_NEXT, and GET_PREV
- for simpler use in multi-threaded programs.
- 2. Changed EXB_SHARED_LOCK_ON in BULLET2.H (was ERR_ rather than EXB_).
-
- 2.042 20-Dec-95:
-
- 1. MAKE_DIR_DOS was expecting directory name in DFP.bufferPtr. Changed to
- expect path in DFP.filenamePtr.
-
- 2.040 28-Oct-95:
-
- 1. BACKUP_FILE_XB extended to back up related memo file on DBF backup.
- 2. [2.033] Fixed memo file problems.
- 3. [2.032] Win95 version available.
- 4. [2.031] DOSX32 version available.
-
- 2.031 11-Sep-95:
-
- 1. Added DP.fieldOffset to documentation for GET_DESCRIPTOR_XB.
- 2. Instance tracker purified of DosEnterCritSec need.
-
- 2.030 9-Sep-95:
-
- 1. Removed erroneous '...header reload...' in online docs of RELOCK_DATA_XB.
- 2. Default maximum filesizes set to 2047 MB from 2048 MB (absolute max is
- 4095 MB).
- 3. Fixed REINDEX_XB so that it actually uses re-evaluted key expression.
- 4. ATEXIT_XB is obsolete.
- 5. BREAK_XB is obsolete.
-
- 2.020 31-Aug-95:
-
- 1. Locality and other cache-related options can be set at file open.
- 2. IP.versionBullet changed to *1000 from *100 (2020 is version 2.020).
- 3. Instance tracker corrected.
-
- 2.01 22-Aug-95:
-
- 1. Changed index header to 1024 bytes from 768 for sector-alignment.
- 2. Changed header ID to '31ch' from '30ch'.
-
- 2.00 20-Aug-95:
-
- Preliminary release.
-
-
- ΓòÉΓòÉΓòÉ 8. Bullet Include File ΓòÉΓòÉΓòÉ
-
- The Bullet/2 C/C++ header file, BULLET2.H:
-
-
- /* BULLET2.H 3-Jan-96-chh
- *
- * Bullet/2 header for 32-bit C/C++
- * Bullet call numbers, parameter packs, and error number equates
- *
- */
-
- #ifndef __BULLET_H
- #define __BULLET_H
-
- /*
- * The #pragma pack(1)/#pragma pack() is no longer required since all
- * structure members in this header will align properly -- all are
- * 32-bit size except for the structure "FieldDescType", but fieldDA
- * member (a LONG) is at a 32-bit alignment already (at byte offset +12).
- * The altFieldLength member, same structure, is also already at
- * proper alignment for a 16-bit value (at byte offset +18). If, for
- * some reason, your compiler aligns the members differently, then you
- * must use the appropriate compiler pragma to prevent this -- the
- * FieldDescType size is 32 bytes exactly. It is not likely that any
- * conforming compiler will alter this structure, but, now you know what
- * to do if it does.
- *
- * #pragma pack(1)
- *
- * NOTE: In your program source code, when you layout your record buffer
- * structure, you must use the #pragma pack(1)/#pragma pack() directives
- * around it since it will be, most likely, modified. The reason is that
- * this structure MUST start with the implicit TAG field (a BYTE), and so,
- * unless you use only BYTE/CHAR members in your structure (Bullet can use
- * binary field values), or take special care to align the record layout
- * so no padding is performed by the compiler, then you will need to use
- * the pack(1) pragma.
- *
- * #pragma pack()
- */
-
- /* the following are assumed defined in OS2DEF.H
- *
- * #define VOID void
- * #define SHORT short
- * #define LONG long
- * #define CHAR char
- *
- * typedef unsigned char BYTE;
- * typedef unsigned short USHORT;
- * typedef unsigned long ULONG;
- * typedef unsigned char *PSZ;
- * typedef VOID *PVOID;
- *
- * typedef unsigned long APIRET;
- * #define APIENTRY _System (right-to-left, caller cleans stack, etc.)
- *
- * Note: Some compilers may name "_System" differently, like __syscall...
- * ...it won't make a difference so long as APIENTRY equates to this
- */
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- LONG APIENTRY BULLET(PVOID datapack);
-
- // Bullet can return negative result code so use LONG rather than APIRET
-
- #ifdef __cplusplus
- }
- #endif
-
- /* All Bullet routines are mutex-semaphore protected except the following:
- *
- * MEMORY_XB STAT_HANDLE_XB GET_ERROR_CLASS_XB
- * QUERY_SYSVARS_XB CHECK_REMOTE_XB
- * STAT_DATA_XB STAT_INDEX_XB
- *
- * This means that any thread can call the above routines at any time. All
- * other calls in the current process block until the previous thread exits
- * BULLET. The default mutex wait is 0 milliseconds, and can be set via
- * SET_SYSVARS_XB using the MUTEX_SEM_TIMEOUT index. In the case of
- * STAT_DATA_XB and STAT_INDEX_XB, these should be used only when there
- * is no chance that another thread may close that file handle while the
- * routine is working.
- *
- */
-
-
-
- /* ************************************************************************
- *
- * xxx.func call numbers
- *
- * ************************************************************************/
-
- #define GEN_ERR_XB 0
- #define INIT_XB 1 /* system */
- #define EXIT_XB 2
- #define MEMORY_XB 4
- #define BACKUP_FILE_XB 6
- #define STAT_HANDLE_XB 7
- #define GET_ERROR_CLASS_XB 8
-
- #define QUERY_SYSVARS_XB 10 /* advanced system */
- #define SET_SYSVARS_XB 11
-
- #define CREATE_DATA_XB 20 /* data control mid-level */
- #define OPEN_DATA_XB 21
- #define CLOSE_DATA_XB 22
- #define STAT_DATA_XB 23
- #define READ_DATA_HEADER_XB 24
- #define FLUSH_DATA_HEADER_XB 25
- #define COPY_DATA_HEADER_XB 26
- #define ZAP_DATA_HEADER_XB 27
-
- #define CREATE_INDEX_XB 30 /* key control mid-level */
- #define OPEN_INDEX_XB 31
- #define CLOSE_INDEX_XB 32
- #define STAT_INDEX_XB 33
- #define READ_INDEX_HEADER_XB 34
- #define FLUSH_INDEX_HEADER_XB 35
- #define COPY_INDEX_HEADER_XB 36
- #define ZAP_INDEX_HEADER_XB 37
-
- #define GET_DESCRIPTOR_XB 40 /* data access mid-level */
- #define GET_RECORD_XB 41
- #define ADD_RECORD_XB 42
- #define UPDATE_RECORD_XB 43
- #define DELETE_RECORD_XB 44
- #define UNDELETE_RECORD_XB 45
- #define PACK_RECORDS_XB 46
- #define DEBUMP_RECORD_XB 47
-
- #define GET_MEMO_SIZE_XB 50 /* memo access mid-level */
- #define GET_MEMO_XB 51
- #define ADD_MEMO_XB 52
- #define UPDATE_MEMO_XB 53
- #define DELETE_MEMO_XB 54
- #define MEMO_BYPASS_XB 59 /* see below for bypass ordinals */
-
- #define BYPASS_CREATE_MEMO 1 /* The bypass routines are automatically */
- #define BYPASS_OPEN_MEMO 2 /* performed by BULLET but can be done */
- #define BYPASS_CLOSE_MEMO 3 /* manually, if needed - these numbers are */
- #define BYPASS_READ_MEMO_HEADER 4 /* put in MDP.memoBypass, with MDP.func */
- #define BYPASS_FLUSH_MEMO_HEADER 5 /* set to MEMO_BYPASS_XB */
-
- #define FIRST_KEY_XB 60 /* key access mid-level */
- #define EQUAL_KEY_XB 61
- #define NEXT_KEY_XB 62
- #define PREV_KEY_XB 63
- #define LAST_KEY_XB 64
- #define STORE_KEY_XB 65
- #define DELETE_KEY_XB 66
- #define BUILD_KEY_XB 67
- #define GET_CURRENT_KEY_XB 68
- #define GET_KEY_FOR_RECORD_XB 69
-
- #define GET_FIRST_XB 70 /* key and data access high-level */
- #define GET_EQUAL_XB 71
- #define GET_NEXT_XB 72
- #define GET_PREV_XB 73
- #define GET_LAST_XB 74
- #define INSERT_XB 75
- #define UPDATE_XB 76
- #define REINDEX_XB 77
-
- #define LOCK_XB 80 /* network control */
- #define UNLOCK_XB 81
- #define LOCK_INDEX_XB 82
- #define UNLOCK_INDEX_XB 83
- #define LOCK_DATA_XB 84
- #define UNLOCK_DATA_XB 85
- #define CHECK_REMOTE_XB 86
- #define RELOCK_XB 87
- #define RELOCK_INDEX_XB 88
- #define RELOCK_DATA_XB 89
-
- #define DELETE_FILE_DOS 90 /* DOS file I/O low-level */
- #define RENAME_FILE_DOS 91
- #define CREATE_FILE_DOS 92
- #define OPEN_FILE_DOS 93
- #define SEEK_FILE_DOS 94
- #define READ_FILE_DOS 95
- #define WRITE_FILE_DOS 96
- #define CLOSE_FILE_DOS 97
- #define ACCESS_FILE_DOS 98
- #define EXPAND_FILE_DOS 99
- #define MAKE_DIR_DOS 100
- #define COMMIT_FILE_DOS 101
-
- /* ************************************************************************
- *
- * operating system file I/O equates
- *
- * ************************************************************************/
-
- #define READONLY 0x00000000 /* std file access mode */
- #define WRITEONLY 0x00000001 /* no underscore used for std equates */
- #define READWRITE 0x00000002
-
- #define DENYREADWRITE 0x00000010 /* std file share mode, cannot be 0 */
- #define DENYWRITE 0x00000020
- #define DENYREAD 0x00000030
- #define DENYNONE 0x00000040
- #define NOINHERIT 0x00000080
-
- #define NO_LOCALITY 0x00000000 /* optional cache modes */
- #define SEQ_LOCALITY 0x00010000
- #define RND_LOCALITY 0x00020000
- #define MIX_LOCALITY 0x00030000
- #define SKIP_CACHE 0x00100000 /* not inherited by child process */
- #define WRITE_THROUGH 0x00400000 /* not inherited by child process */
-
-
- #define LOCK_SHARED 1 /* for LP.xlMode and LP.dlMode */
- #define LOCK_EXCLUSIVE 0
-
- /* ************************************************************************
- *
- * .sortFunction IDs, Query/Set item IDs
- *
- * ************************************************************************/
-
- #define CTRYCODE 0 /* signifies default country code (at index create) */
- #define CODEPAGE 0 /* signifies default code page (at index create) */
-
- #define DUPS_ALLOWED (1 << 16) /* allow duplicate keys (.sortFunction flag) */
-
- /* All Bullet system vars set to default values at INIT_XB */
- /* Sorts 1-19 also used as CIP.sortFunction (can be OR'ed with DUPS_ALLOWED) */
- /* Intrinsic sorts (1-6) are read-only (R-O) */
-
- #define ASCII_SORT 1 /* sort by: ASCII value (R-O) */
- #define NLS_SORT 2 /* NLS (R-O) */
- #define S16_SORT 3 /* 16-bit signed integer (R-O) */
- #define U16_SORT 4 /* 16-bit unsigned integer (R-O) */
- #define S32_SORT 5 /* 32-bit signed integer (R-O) */
- #define U32_SORT 6 /* 32-bit unsigned integer (R-O) */
-
- /* sorts 7 to 9 are reserved */
- /* Custom sort-compare functions are from 10 to 19 */
-
- #define BUILD_KEY_FUNC 20 /* key build function ptr */
- #define PARSER_FUNC 21 /* key expression parser function ptr */
-
- #define MUTEX_SEM_HANDLE 29 /* handle of Bullet's mutex semaphore (R-O) */
- #define LOCK_TIMEOUT 30 /* lock-wait timeout (default=0, no wait)*/
- #define MUTEX_SEM_TIMEOUT 31 /* mutex semaphore-wait timeout (def=0,none) */
- #define PACK_BUFFER_SIZE 32 /* pack buffer size (def=0, min autosize) */
- #define REINDEX_BUFFER_SIZE 33 /* reindex buffer size (def=0, min autosize) */
- #define REINDEX_PACK_PCT 34 /* reindex node pack % (default=100, max) */
- #define TMP_PATH_PTR 35 /* temporary file path ptr (default=NULL) */
- #define REINDEX_SKIP_TAG 36 /* index skip tag select (default=0, none) */
- #define COMMIT_AT_EACH 37 /* commit each insert/update in pack (def=0) */
- #define MEMO_BLOCKSIZE 38 /* memo block size (default=512 bytes) */
- #define MEMO_EXTENSION 39 /* memo filename extension (default='DBT\0') */
- #define MAX_DATAFILE_SIZE 40 /* max data size (default=0x7FEFFFFF=2095MB) */
- #define MAX_INDEXFILE_SIZE 41 /* max index size (default=0x7FEFFFFF=2095MB)*/
- #define ATOMIC_MODE 42 /* bit0=1 atomic next/prev key access (def=0)*/
-
- /* ************************************************************************
- *
- * Parameter pack structures, typedefs
- *
- * ************************************************************************/
-
- /* AP, CP, CDP, etc., are suggested variable names */
-
- typedef struct _ACCESSPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file to access */
- LONG recNo; /* IO, record number */
- PVOID recPtr; /* I, programmer's record buffer */
- PVOID keyPtr; /* I, programmer's key buffer */
- PVOID nextPtr; /* I, NULL if not xaction, else next AP in list */
- } ACCESSPACK; /* AP */
- typedef ACCESSPACK *PACCESSPACK;
-
- typedef struct _COPYPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file to copy */
- PSZ filenamePtr; /* I, filename to use (drv+path must exist if used) */
- } COPYPACK; /* CP */
- typedef COPYPACK *PCOPYPACK;
-
- typedef struct _CREATEDATAPACK {
- ULONG func;
- ULONG stat;
- PSZ filenamePtr; /* I, filename to use */
- ULONG noFields; /* I, 1 to 254 */
- PVOID fieldListPtr; /* I, descriptor list, 1 per field */
- ULONG fileID; /* I, 0x03 for standard DBF, 0x8B if memo file also */
- } CREATEDATAPACK; /* CDP */
- typedef CREATEDATAPACK *PCREATEDATAPACK;
-
- typedef struct _CREATEINDEXPACK {
- ULONG func;
- ULONG stat;
- PSZ filenamePtr; /* I, filename to use */
- PSZ keyExpPtr; /* I, e.g., "SUBSTR(LNAME,1,4)+SSN" */
- LONG xbLink; /* I, opened data file handle this indexes */
- ULONG sortFunction; /* I, 1-9 system, 10-19 custom */
- ULONG codePage; /* I, 0=use process default */
- ULONG countryCode; /* I, 0=use process default */
- PVOID collatePtr; /* I, NULL=use cc/cp else use passed table for sort */
- ULONG nodeSize; /* I, 512, 1024, or 2048 */
- } CREATEINDEXPACK; /* CIP */
- typedef CREATEINDEXPACK *PCREATEINDEXPACK;
-
- typedef struct _FIELDDESCTYPE {
- BYTE fieldName[11]; /* IO, upper A-Z and _; 1-10 chars, 0-filled, 0-term */
- BYTE fieldType; /* IO, C,D,L,N, or M */
- LONG fieldDA; /* x, offset within record (run-time storage option) */
- BYTE fieldLen; /* IO, C=1-255,D=8,L=1,N=1-19,M=10 */
- BYTE fieldDC; /* IO, fieldType=N then 0-15 else 0 */
- USHORT altFieldLength;/* IO, 0 */
- BYTE filler[12]; /* I, 0 */
- } FIELDDESCTYPE; /* nested in _DESCRIPTORPACK */
- typedef FIELDDESCTYPE *PFIELDDESCTYPE;
-
- typedef struct _DESCRIPTORPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of DBF file */
- ULONG fieldNumber; /* IO, first field is 1 */
- ULONG fieldOffset; /* O, offset of field within record (tag=offset 0) */
- FIELDDESCTYPE FD; /* IO FD.fieldName only, O for rest of FD */
- } DESCRIPTORPACK; /* DP */
- typedef DESCRIPTORPACK *PDESCRIPTORPACK;
-
- typedef struct _DOSFILEPACK {
- ULONG func;
- ULONG stat;
- PSZ filenamePtr; /* I, filename to use */
- ULONG handle; /* IO, handle of open file */
- ULONG asMode; /* I, access-sharing mode */
- ULONG bytes; /* IO, bytes to read, write, length of */
- LONG seekTo; /* IO, seek to offset, current offset */
- ULONG method; /* I, seek method (0=start of file, 1=current, 2=end) */
- PVOID bufferPtr; /* I, buffer to read into or write from */
- ULONG attr; /* I, attribute to create file with */
- PSZ newNamePtr; /* I, name to use on rename */
- } DOSFILEPACK; /* DFP */
- typedef DOSFILEPACK *PDOSFILEPACK;
-
- typedef struct _EXITPACK {
- ULONG func;
- ULONG stat;
- } EXITPACK; /* EP */
- typedef EXITPACK *PEXITPACK;
-
- typedef struct _HANDLEPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file */
- } HANDLEPACK; /* HP */
- typedef HANDLEPACK *PHANDLEPACK;
-
- typedef struct _INITPACK {
- ULONG func;
- ULONG stat;
- ULONG JFTsize; /* I, max opened files (20-1024+) */
- ULONG versionDOS; /* O, e.g., 230 for 2.30 */
- ULONG versionBullet; /* O, e.g., 2019 for 2.019 */
- ULONG versionOS; /* O, e.g., 4=OS/2 32-bit */
- PVOID exitPtr; /* O, function pointer to EXIT_XB routine */
- } INITPACK; /* IP */
- typedef INITPACK *PINITPACK;
-
- typedef struct _LOCKPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file to lock */
- ULONG xlMode; /* I, index lock mode (0=exclusive, 1=shared) */
- ULONG dlMode; /* I, data lock mode (0=exclusive, 1=shared) */
- LONG recStart; /* I, if data, first record # to lock, or 0 for all */
- ULONG recCount; /* I, if data and recStart!=0, # records to lock */
- PVOID nextPtr; /* I, NULL if not xaction, else next LP in list */
- } LOCKPACK; /* LP */
- typedef LOCKPACK *PLOCKPACK;
-
- typedef struct _MEMODATAPACK {
- ULONG func;
- ULONG stat;
- ULONG dbfHandle; /* I, handle of DBF file to which this memo file belongs */
- ULONG memoBypass; /* I, memo bypass function to do, if any */
- PVOID memoPtr; /* I, ptr to memo record buffer */
- ULONG memoNo; /* IO, memo record number (aka block number) */
- ULONG memoOffset; /* I, position within record to start read/update */
- ULONG memoBytes; /* IO, number of bytes to read/update */
- } MEMODATAPACK; /* MDP */
- typedef MEMODATAPACK *PMEMODATAPACK;
-
- typedef struct _MEMORYPACK {
- ULONG func;
- ULONG stat;
- ULONG memory; /* O, not used in OS/2 */
- } MEMORYPACK; /* MP */
- typedef MEMORYPACK *PMEMORYPACK;
-
- typedef struct _OPENPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* O, handle of file opened */
- PSZ filenamePtr; /* I, Bullet file to open */
- ULONG asMode; /* I, access-sharing-cache mode */
- LONG xbLink; /* I, if index open, xbLink=handle of its opened DBF */
- } OPENPACK; /* OP */
- typedef OPENPACK *POPENPACK;
-
- typedef struct _QUERYSETPACK {
- ULONG func;
- ULONG stat;
- ULONG item; /* I, Bullet sysvar item to get/set */
- ULONG itemValue; /* IO, current/new value */
- } QUERYSETPACK; /* QSP */
- typedef QUERYSETPACK *PQUERYSETPACK;
-
- typedef struct _REMOTEPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of file, or if 0, use RP.drive */
- ULONG drive; /* I, drive (1=A,2=B,3=C,...0=current) to check */
- ULONG isRemote; /* O, =1 of handle/drive is remote, =0 if local */
- ULONG flags; /* O, 0 */
- ULONG isShare; /* O, 1 */
- } REMOTEPACK; /* RP */
- typedef REMOTEPACK *PREMOTEPACK;
-
- typedef struct _STATDATAPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle to check */
- ULONG fileType; /* O, bit0=1 data file */
- ULONG flags; /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
- ULONG progress; /* O, 0,1-99% pack progress */
- PVOID morePtr; /* O, 0 */
- ULONG fields; /* O, fields per record */
- ULONG asMode; /* O, access-sharing-cache mode */
- PSZ filenamePtr; /* O, filename used in open */
- ULONG fileID; /* O, first byte of DBF file */
- ULONG lastUpdate; /* O, high word=year,low byte=day, high byte=month */
- ULONG records; /* O, data records (including "deleted") */
- ULONG recordLength; /* O, record length */
- ULONG xactionFlag; /* O, 0 */
- ULONG encryptFlag; /* O, 0 */
- PVOID herePtr; /* O, this file's control address */
- ULONG memoHandle; /* O, handle of open memo file (0 if none) */
- ULONG memoBlockSize; /* O, memo file block size */
- ULONG memoFlags; /* O, bit0=1 dirty */
- ULONG memoLastRecord; /* O, last accessed memo record (0 if none) */
- ULONG memoLastSize; /* O, size of last accessed memo record (in bytes, +8) */
- ULONG lockCount; /* O, number of full-locks in force */
- } STATDATAPACK; /* SDP */
- typedef STATDATAPACK *PSTATDATAPACK;
-
- typedef struct _STATHANDLEPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle to check */
- LONG ID; /* O, bit0=1 data file, bit0=1 index file */
- } STATHANDLEPACK; /* SHP */
- typedef STATHANDLEPACK *PSTATHANDLEPACK;
-
- typedef struct _STATINDEXPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle to check */
- ULONG fileType; /* O, bit0=0 index file */
- ULONG flags; /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
- ULONG progress; /* O, 0,1-99% reindex progress */
- PVOID morePtr; /* O, 0 */
- ULONG xbLink; /* O, XB file link handle */
- ULONG asMode; /* O, access-sharing-cache mode */
- PSZ filenamePtr; /* O, pointer to filename used in open */
- ULONG fileID; /* O, "31ch" */
- PSZ keyExpPtr; /* O, pointer to key expression */
- ULONG keys; /* O, keys in file */
- ULONG keyLength; /* O, key length */
- ULONG keyRecNo; /* O, record number of current key */
- PVOID keyPtr; /* O, ptr to current key value (valid to keyLength) */
- PVOID herePtr; /* O, this file's control address */
- ULONG codePage; /* O, code page at create time */
- ULONG countryCode; /* O, country code at create time */
- PVOID CTptr; /* O, collate table ptr, NULL=no collate table present */
- ULONG nodeSize; /* O, node size */
- ULONG sortFunction; /* O, sort function ID */
- ULONG lockCount; /* O, number of full-locks in force */
- } STATINDEXPACK; /* SIP */
- typedef STATINDEXPACK *PSTATINDEXPACK;
-
- typedef struct _XERRORPACK {
- ULONG func;
- ULONG stat; /* I, error to check */
- ULONG errClass; /* O, class of error */
- ULONG action; /* O, action recommended for error */
- ULONG location; /* O, location of error */
- } XERRORPACK; /* XEP */
- typedef XERRORPACK *PXERRORPACK;
-
-
- /* ************************************************************************
- *
- * Error codes
- *
- * ************************************************************************/
-
- #define EXB_NOT_ENOUGH_MEMORY 8 /* cannot get memory requested */
- #define EXB_UNEXPECTED_EOF 38 /* unexpect EOF (bytes read != bytes asked) */
- #define EXB_DISK_FULL 39 /* disk full on WriteFile */
- #define EXB_FILE_EXISTS 80 /* cannot create file since it already exists */
-
- /* Other operating system errors are as returned by OS itself */
-
- /* System/general error codes */
-
- #define EXB_OR_WITH_FAULTS 8192 /* 8192+1 to +4, close-type errors */
- #define EXB_ILLEGAL_CMD 8300 /* function not allowed */
- #define EXB_OLD_DOS 8301 /* OS version < MIN_DOS_NEEDED */
- #define EXB_NOT_INITIALIZED 8302 /* init not active, must do INIT_XB */
- #define EXB_ALREADY_INITIALIZED 8303 /* init already active, must do EXIT_XB */
- #define EXB_TOO_MANY_HANDLES 8304 /* more than 1024 opens requested */
- #define EXB_SYSTEM_HANDLE 8305 /* Bullet won't use or close handles 0-2 */
- #define EXB_FILE_NOT_OPEN 8306 /* file not open (not Bullet handle, including xbLink) */
- #define EXB_FILE_IS_DIRTY 8307 /* tried to reload header but current still dirty */
- #define EXB_BAD_FILETYPE 8308 /* tried key op on non-key file, data op on non... */
- #define EXB_TOO_MANY_PACKS 8309 /* too many INSERT,UPDATE,REINDEX,LOCK_XB packs */
- #define EXB_NULL_RECPTR 8310 /* null record pointer passed to Bullet */
- #define EXB_NULL_KEYPTR 8311 /* null key pointer passed to Bullet */
- #define EXB_NULL_MEMOPTR 8312 /* null memo pointer passed to Bullet */
- #define EXB_EXPIRED 8313 /* evaluation time period has expired */
- #define EXB_BAD_INDEX 8314 /* Query/SetSysVars index beyond last one */
- #define EXB_RO_INDEX 8315 /* SetSysVar index item is read-only */
- #define EXB_FILE_BOUNDS 8316 /* file size > 4GB, or > system var sets */
-
- /* Multi-access error codes */
-
- #define EXB_BAD_LOCK_MODE 8401 /* lock mode (LP) not valid */
- #define EXB_NOTHING_TO_RELOCK 8402 /* cannot relock without existing full-lock */
- #define EXB_SHARED_LOCK_ON 8403 /* write access needed but lock is shared (flush on backup) */
-
- /* Index error codes */
-
- #define EXB_KEY_NOT_FOUND 8501 /* exact match of key not found */
- #define EXB_KEY_EXISTS 8502 /* key exists already and dups not allowed */
- #define EXB_END_OF_FILE 8503 /* already at last index order */
- #define EXB_TOP_OF_FILE 8504 /* already at first index order */
- #define EXB_EMPTY_FILE 8505 /* nothing to do since no keys */
- #define EXB_CANNOT_GET_LAST 8506 /* cannot locate last key */
- #define EXB_BAD_INDEX_STACK 8507 /* index file is corrupt */
- #define EXB_BAD_INDEX_READ0 8508 /* index file is corrupt */
- #define EXB_BAD_INDEX_WRITE0 8509 /* index file is corrupt */
-
- #define EXB_OLD_INDEX 8521 /* old index, run through ReindexOld to update */
- #define EXB_UNKNOWN_INDEX 8522 /* not a Bullet index file */
- #define EXB_KEY_TOO_LONG 8523 /* keylength > 62 (or 64 if unique), or is 0 */
-
- #define EXB_PARSER_NULL 8531 /* parser function pointer is NULL */
- #define EXB_BUILDER_NULL 8532 /* build key function pointer is NULL */
- #define EXB_BAD_SORT_FUNC 8533 /* CIP.sortFunction not valid */
- #define EXB_BAD_NODE_SIZE 8534 /* CIP.nodeSize is not 512, 1024, or 2048 */
- #define EXB_FILENAME_TOO_LONG 8535 /* CIP.filenamePtr->pathname > max path length */
-
- #define EXB_KEYX_NULL 8541 /* expression is effectively NULL */
- #define EXB_KEYX_TOO_LONG 8542 /* CIP.keyExpPtr->expression > 159 */
- #define EXB_KEYX_SYM_TOO_LONG 8543 /* fieldname/funcname in expression > 10 chars */
- #define EXB_KEYX_SYM_UNKNOWN 8544 /* fieldname/funcname in expression unknown */
- #define EXB_KEYX_TOO_MANY_SYMS 8545 /* too many symbols/fields used in expression */
- #define EXB_KEYX_BAD_SUBSTR 8546 /* invalid SUBSTR() operand in expression */
- #define EXB_KEYX_BAD_SUBSTR_SZ 8547 /* SUBSTR() exceeds field's size */
- #define EXB_KEYX_BAD_FORM 8548 /* didn't match expected symbol in expression */
-
- #define EXB_NO_READS_FOR_RUN 8551 /* unlikely, use different reindex buffer size */
- #define EXB_TOO_MANY_RUNS 8552 /* unlikely, too many runs (64K or more runs) */
- #define EXB_TOO_MANY_RUNS_FOR_BUFFER 8553 /* unlikely, too many runs for run buffer */
- #define EXB_TOO_MANY_DUPLICATES 8554 /* more than 64K "identical" keys */
-
- #define EXB_INSERT_RECNO_BAD 8561 /* AP.recNo cannot be > 0 if inserting */
- #define EXB_PREV_APPEND_EMPTY 8562 /* no prev append for insert yet AP.recNo==80000000h */
- #define EXB_PREV_APPEND_MISMATCH 8563 /* prev append's xbLink does not match this */
- #define EXB_INSERT_KBO_FAILED 8564 /* could not back out key at INSERT_XB */
- #define EXB_INSERT_DBO_FAILED 8565 /* could not back out data records at INSERT_XB */
-
- #define WRN_NOTHING_TO_UPDATE 8571 /* all AP.recNo=0 at UPDATE_XB */
- #define EXB_INTERNAL_UPDATE 8572 /* internal error UPDATE_XB, not in hdl/rec# list */
-
- #define EXB_FAILED_DATA_RESTORE 8573 /* could not restore original data record (*) */
- #define EXB_FAILED_KEY_DELETE 8574 /* could not remove new key (*) */
- #define EXB_FAILED_KEY_RESTORE 8575 /* could not restore original key(*) */
- /* *original error, which forced a back-out, has been replaced by this error */
- /* this error is always returned in the first AP.stat (-1 on data, 1 on index) */
-
- /* Data error codes */
-
- #define EXB_EXT_XBLINK 8601 /* xbLink handle is not internal DBF (is -1) */
- #define EXB_FIELDNAME_TOO_LONG 8602 /* fieldname is > 10 characters */
- #define EXB_RECORD_TOO_LONG 8603 /* record length is > 64K */
- #define EXB_FIELD_NOT_FOUND 8604 /* fieldname not found in descriptor info */
- #define EXB_BAD_FIELD_COUNT 8605 /* fields <= 0 or >= MAX_FIELDS (Init,Open) */
- /* and also GetDescriptor by field number */
- #define EXB_BAD_HEADER 8606 /* bad header (reclen=0, etc., from LocateTo, Flush) */
- #define EXB_BUFFER_TOO_SMALL 8607 /* buffer too small (pack buffer < reclen in pack) */
- #define EXB_INTERNAL_PACK 8608 /* internal error in PackRecords */
- #define EXB_BAD_RECNO 8609 /* record number=0 or > records in data file hdr */
- /* or Pack on empty data file */
- #define WRN_RECORD_TAGGED 8610 /* record's tag field matches skip tag */
-
- /* Memo error codes */
-
- #define WRN_CANNOT_OPEN_MEMO 8701 /* DBF says memo file but memo open fails */
- #define EXB_MEMO_NOT_OPEN 8702 /* no open memo file for operation */
- #define EXB_BAD_BLOCKSIZE 8703 /* memo blocksize must be at least 24 bytes */
- #define EXB_MEMO_DELETED 8704 /* memo is deleted */
- #define EXB_MEMO_PAST_END 8705 /* memo data requested is past end of record */
- #define EXB_BAD_MEMONO 8706 /* memo number is not valid */
- #define EXB_MEMO_IN_USE 8707 /* memo add encountered likely corrupt memo file */
- #define EXB_BAD_AVAIL_LINK 8708 /* memo avail link cannot be valid (is 0) */
- #define EXB_MEMO_ZERO_SIZE 8709 /* memo data has no size */
- #define EXB_MEMO_IS_SMALLER 8710 /* memo attempt to shrink but already <= size */
-
- #endif /* ifndef __BULLET_H */
-
-
- ΓòÉΓòÉΓòÉ 9. Bullet Functions ΓòÉΓòÉΓòÉ
-
- System level Advanced system-level
-
- INIT_XB QUERY_SYSVARS_XB
- EXIT_XB SET_SYSVARS_XB
- MEMORY_XB
- BACKUP_FILE_XB
- STAT_HANDLE_XB
- GET_ERROR_CLASS_XB
-
- Data low-level Data mid-level Memo mid-level
-
- CREATE_DATA_XB GET_DESCRIPTOR_XB GET_MEMO_SIZE_XB
- OPEN_DATA_XB GET_RECORD_XB GET_MEMO_XB
- CLOSE_DATA_XB ADD_RECORD_XB ADD_MEMO_XB
- STAT_DATA_XB UPDATE_RECORD_XB UPDATE_MEMO_XB
- READ_DATA_HEADER_XB DELETE_RECORD_XB DELETE_MEMO_XB
- FLUSH_DATA_HEADER_XB UNDELETE_RECORD_XB MEMO_BYPASS_XB
- COPY_DATA_HEADER_XB PACK_RECORDS_XB
- ZAP_DATA_HEADER_XB DEBUMP_RECORD_XB
-
- Index low-level Index mid-level Index high-level
-
- CREATE_INDEX_XB FIRST_KEY_XB GET_FIRST_XB
- OPEN_INDEX_XB EQUAL_KEY_XB GET_EQUAL_XB
- CLOSE_INDEX NEXT_KEY_XB GET_NEXT_XB
- STAT_INDEX_XB PREV_KEY_XB GET_PREV_XB
- READ_INDEX_HEADER_XB LAST_KEY_XB GET_LAST_XB
- FLUSH_INDEX_HEADER_XB STORE_KEY_XB INSERT_XB
- COPY_INDEX_HEADER_XB DELETE_KEY_XB UPDATE_XB
- ZAP_INDEX_HEADER_XB BUILD_KEY_XB REINDEX_XB
- GET_CURRENT_KEY_XB
- GET_KEY_FOR_RECORD_XB
-
- Network level CP level
-
- LOCK_XB DELETE_FILE_DOS
- UNLOCK_XB RENAME_FILE_DOS
- LOCK_INDEX_XB CREATE_FILE_DOS
- UNLOCK_INDEX_XB OPEN_FILE_DOS
- LOCK_DATA_XB SEEK_FILE_DOS
- UNLOCK_DATA_XB READ_FILE_DOS
- CHECK_REMOTE_XB WRITE_FILE_DOS
- RELOCK_XB CLOSE_FILE_DOS
- RELOCK_INDEX_XB ACCESS_FILE_DOS
- RELOCK_DATA_XB EXPAND_FILE_DOS
- MAKE_DIR_DOS
- COMMIT_FILE_DOS
-
-
- ΓòÉΓòÉΓòÉ 9.1. INIT_XB ΓòÉΓòÉΓòÉ
-
- Pack: INITPACK Source Example
-
- IN OUT
- IP.func IP.stat
- IP.JFTsize IP.versionDOS
- IP.versionBullet
- IP.versionOS
- IP.exitPtr
-
- This must be the first routine called. If it has already been called the
- system variables are restored to their defaults, and an error is returned.
- Otherwise, the entire Bullet system is initialized, and EXIT_XB is registered
- with the OS ExitList handler (DosExitList).
-
- For more than the default open files (generally 20), set IP.JFTsize to the
- total number of concurrently open files you need. Depending on your version,
- Bullet manages up to 1024 Bullet files per process (total data and index; memo
- files are not counted against this total). Setting this less than 20 does
- nothing. This number is for Bullet files, your files, pipes -- anything using
- a handle. If you need to account for handles that you are managing, you
- should add those to IP.JFTsize. For example, if you need 10 data files, each
- with a memo file, and 2 index files per data file, that is 40 total Bullet
- files. If you need to use 15 other handles, for whatever use, add that number
- to the 40 Bullet files, for a total setting of 55. The OS also uses 3 handles
- for itself, so, for all these, IP.JFTsize=58 would be the minimum. You can
- set it higher, but unused handles are wasted handles. In addition, if the
- current process has fewer total handles available than the number you
- specified in IP.JFTsize, Bullet sets the total available handles to IP.JFTsize
- (as the absolute number of handles required). If the current process already
- has more total handles than IP.JFTsize, no action is taken.
-
- On return (where no error occurred), the operating system version is in
- IP.versionDOS (*100) and the Bullet version (*1000) in IP.versionBullet.
- IP.versionOS return is based on the following table:
-
- Bullet Platform IP.versionOS
- MS-DOS 16-bit 0
- Win3x 16-bit 1
- DOSX 32-bit 3
- OS/2 32-bit 4
- WinNT/Win9x 32-bit 5
-
- Not all platforms may be currently available
-
- IP.exitPtr returns with the function pointer to the EXIT_XB routine. This
- function pointer is redundant unless specifically mentioned as being required
- for your platform. It is not needed in OS/2.
-
- Note: References under OUT using *AP.keyPtr or similar (note then *) are used
- throughout this manual and indicate that Bullet updates the contents at
- AP.keyPtr with data (i.e., Bullet filled the buffer).
-
-
- ΓòÉΓòÉΓòÉ 9.2. EXIT_XB ΓòÉΓòÉΓòÉ
-
- Pack: EXITPACK Source Example
-
- IN OUT
- EP.func EP.stat
-
- Call EXIT_XB before ending your program to release any remaining resources
- back to the OS. Open files should be closed by using CLOSE_DATA_XB and
- CLOSE_INDEX_XB. EXIT_XB closes any Bullet files that are still open.
-
- This routine is registered with the operating system and so is called
- automatically when your program terminates.
-
-
- ΓòÉΓòÉΓòÉ 9.3. ATEXIT_XB ΓòÉΓòÉΓòÉ
-
- This routine is obsolete. In OS/2, the EXIT_XB shutdown procedure is
- registered with the operating system. For those systems that do not offer this
- feature in the OS, the compiler run-time routine atexit() is used immediately
- after calling INIT_XB, using IP.exitPtr as the function pointer for atexit().
-
-
- ΓòÉΓòÉΓòÉ 9.4. MEMORY_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMORYPACK Source Example
-
- IN OUT
- MP.func MP.stat
- MP.memory
-
- Returns the largest memory block of unused physical memory.
-
- OS/2 uses virtual memory. Therefore, returning the amount of 'free' memory is
- not indicative of anything useful, other than the amount of memory being
- unused, or actually, wasted. Wasted because it is always better for that
- memory to be used for something, than for it to 'stand by' just waiting to be
- used.
-
- Future versions may return a more useful value.
-
- Note: This routine is not mutex-protected.
-
-
- ΓòÉΓòÉΓòÉ 9.5. BACKUP_FILE_XB ΓòÉΓòÉΓòÉ
-
- Pack: COPYPACK Source Example
-
- IN OUT
- CP.func CP.stat
- CP.handle
- CP.filenamePtr
-
- Copy an open BULLET index file or data/memo files. BULLET repacks and
- reindexes files in place, requiring less disk space to perform the function.
- This routine allows a file to be safely copied for a possible later restore.
-
- This function is recommended prior to packing a data file with
- PACK_RECORDS_XB. For index files, COPY_INDEX_HEADER_XB is sufficient since
- index files are easily recreated so long as you have the data file along with
- the index file header.
-
- A full-lock should be in force before copying. A shared lock may be used.
-
- If CP.handle belongs to a DBF data file, and if a memo file is attached, the
- memo file backup name is as CP.filenamePtr, the backup DBF pathname, but the
- extension is always set to "._BT". For example, if CP.handle is for a DBF
- that has a DBT memo file attached, then the current state of the DBF file is
- copied to CP.filenamePtr, say, "\CURRBACK\ACCT.DBF", and, in this case, the
- DBT memo file is copied to "\CURRBACK\ACCT._BT". The name of the original
- DBF/DBT does not matter. If MEMO_EXTENSION of SET_SYSVARS_XB has changed the
- default, then that extension is used on the memo copy, with '_' replacing its
- first character.
-
- To prevent the backing up of a DBT memo file when backing up a DBF data file,
- set CP.handle = -CP.handle (i.e., negative CP.handle). This way, the DBT memo
- file is not copied. To backup only a DBT file, close the DBF and copy the DBT
- by some other means.
-
-
- ΓòÉΓòÉΓòÉ 9.6. STAT_HANDLE_XB ΓòÉΓòÉΓòÉ
-
- Pack: STATHANDLEPACK Source Example
-
- IN OUT
- SHP.func SHP.stat
- SHP.handle SHP.ID
-
- Get information on a file handle number to determine if it is a BULLET file,
- and if so, its type: index or data.
-
- SHP.ID File type
- 0 index, IX3 use STAT_INDEX_XB for file stats
- 1 data, DBF use STAT_DATA_XB for file stats
- -1 unknown
-
- Only bit0 of SHP.ID is significant if not -1. So, if bit0=0 then the handle
- belongs to an index file. If bit0=1 then it's a data file.
-
- Memo file handles return as unknown. A DBF file's memo file handle is stored
- in the DBF file's data area, and is returned by STAT_DATA_XB in
- SDP.memoHandle.
-
- Note: This routine is not mutex-protected.
-
-
- ΓòÉΓòÉΓòÉ 9.7. GET_ERROR_CLASS_XB ΓòÉΓòÉΓòÉ
-
- Pack: XERRORPACK Source Example
-
- IN OUT
- XEP.func XEP.errClass
- XEP.stat XEP.action
- XEP.location
-
- Get the extended error information for the code passed in XEP.stat. This
- information includes the error classification, recommended action, and origin
- of the error.
-
- Any system error code can be specified, not necessarily the one that last
- occurred. If a return code is not a BULLET code, then it is a system error
- code (from the CP, DosXXX routines).
-
- The ERRCLASS, ERRACT, and ERRLOC items below are OS/2 values, names and
- descriptions for DosErrClass().
-
- Error Classification
-
- Value Name Description
- 1 ERRCLASS_OUTRES Out of resources
- 2 ERRCLASS_TEMPSIT Temporary situation
- 3 ERRCLASS_AUTH Authorization failed
- 4 ERRCLASS_INTRN Internal error
- 5 ERRCLASS_HRDFAIL Device hardware failure
- 6 ERRCLASS_SYSFAIL System failure
- 7 ERRCLASS_APPEAR Probably application error
- 8 ERRCLASS_NOTFND Item not located
- 9 ERRCLASS_BADFMT Bad format for function or data
- 10 ERRCLASS_LOCKED Resource or data locked
- 11 ERRCLASS_MEDIA Incorrect media, CRC error
- 12 ERRCLASS_ALREADY Action already taken or done, or resource already exists
- 13 ERRCLASS_UNK Unclassified
- 14 ERRCLASS_CANT Cannot perform requested action
- 15 ERRCLASS_TIME Timeout
-
-
- Recommended Action
-
- Value Name Description
- 1 ERRACT_RETRY Retry immediately
- 2 ERRACT_DLYRET Delay and retry
- 3 ERRACT_USER Bad user input - get new values
- 4 ERRACT_ABORT Terminate in an orderly manner
- 5 ERRACT_PANIC Terminate immediately
- 6 ERRACT_IGNORE Ignore error
- 7 ERRACT_INTRET Retry after user intervention
-
-
- Origin
-
- Value Name Description
- 1 ERRLOC_UNK Unknown
- 2 ERRLOC_DISK Disk
- 3 ERRLOC_NET Network
- 4 ERRLOC_SERDEV Serial device
- 5 ERRLOC_MEM Memory
-
- Note: This routine is not mutex-protected.
-
-
- ΓòÉΓòÉΓòÉ 9.8. QUERY_SYSVARS_XB ΓòÉΓòÉΓòÉ
-
- Pack: QUERYSETPACK Source Example
-
- IN OUT
- QSP.func QSP.stat
- QSP.item QSP.itemValue
-
- Query a BULLET system variable.
-
- To get the function pointers to the sort-compare functions, use:
-
- QSP.item FuncPtr To
- 1 ASCII sort compare
- 2 NLS sort compare
- 3 16-bit signed integer
- 4 16-bit unsigned integer
- 5 32-bit signed integer
- 6 32-bit unsigned integer
- 7-9 Reserved
-
- All intrinsic sort compares (1-6) point to the same function. They cannot be
- called except by BULLET itself. The integer compare routines are based on
- Intel byte order. For Motorola byte order, ASCII sort can be used for
- all-positive numbers, otherwise a custom sort-compare should be used.
-
- 10-19 Custom sort-compare functions
-
- Before creating or opening an index file with a custom sort-compare function
- (which is specified during CREATE_INDEX_XB), that function's address must
- first be sent to BULLET using SET_SYSVARS_XB. Thereafter, that function must
- be available whenever that index file is accessed. See Custom Sort-Compare
- Function for creating custom sort-compare functions.
-
- To get the function pointers to the build key and expression parser routines,
- use:
-
- QSP.item FuncPtr To
- 20 Build key routine
- 21 Key expression parser routine
- 22-28 Reserved
-
- Before creating or opening an index file with a custom build key or expression
- parser routine (which is specified at any time, but must be used in a
- consistent manner), that routine's address must first be sent to BULLET using
- SET_SYSVARS_XB. Thereafter, that routine should be available since it may be
- required again. See Custom Build-Key Routine for creating a custom build-key
- routine and Custom Expression Parser Routine for creating a custom key
- expression parser.
-
- To get the BULLET system variables' values, use:
-
- QSP.item Value To
- 29 (read-only) BULLET mutual-exclusion (mutex) semaphore handle
- 30 Lock file region timeout, in milliseconds (default=0)
- 31 Mutex semaphore request timeout, in milliseconds (default=0)
- 32 Pack buffer size, in bytes (default=0: autosize)
- 33 Reindex buffer size, in bytes (default=0: autosize)
- 34 Reindex node pack percentage, 50-100% (default=100)
- 35 Temporary file path pointer (default=NULL, where TMP= used, then .\)
- 36 Reindex tag field character to skip (default=0, no skip)
- 37 Commit each individual file during INSERT/UPDATE_XB (default=0, defer until flush)
- 38 Memo file block size (default=512 bytes; minimum is 24 bytes)
- 39 Memo file extension (default is "DBT\0")
- 40 Max data file size-1 (default=2047MB, absolute max is 4095MB)
- 41 Max index file size-1 (default=2047MB, absolute max is 4095MB)
- 42 Atomic mode (bit0=1 then atomic Next and Prev access, default=0)
-
- The timeout values determine if the kernel should wait for a pre-determined
- time before returning an error if the resource cannot be obtained. The lock
- timeout specifies how long to wait for a lock to be obtained in case some
- other process has a lock on the same resource. The mutex timeout specifies
- how long to wait for access to BULLET in case some other thread in this
- process is in BULLET. Multiple processes can access BULLET at the same time,
- but only one thread in each process can be inside BULLET at any one time.
-
- The buffer sizes, when 0, default to a minimum reasonable size. Performance
- is acceptable at these sizes. For best performance, provide as much real
- memory as possible, up to 512KB. Larger buffers can be used.
-
- The reindex node pack percentage determines how many keys are packed on a
- node. 100% forces as many keys as possible, minus 1.
-
- If the temporary file path pointer is NULL (the default), then the TMP=
- environment variable is used to locate any temporary files created by BULLET,
- or if that is not found, then the current directory is used. The pointer
- supplied, if any, should be to a string containing an existing path (drive
- should be included; a trailing '\' is optional, but recommended). See
- REINDEX_XB for size requirements.
-
- The reindex skip tag character, if encountered in the DBF record's tag field
- (the first byte of each record), causes the reindex routine to not place that
- record's key value into the index file. Also, BUILD_KEY_XB returns a warning
- if the record supplied has a matching tag character. To disable skip tag
- processing, set it to 0.
-
- Inserts and Updates, by default, do not commit each file when that pack is
- processed. Instead, it is left to the programmer to issue a FLUSH_XB to
- commit. To force a commit after each pack file is processed, set CommitAtEach
- to 1. This is not one single commit, but a commit for each file in the pack,
- after that file has been processed, but before the next file in the pack is.
- This will not prevent a roll-back should it be needed.
-
- A memo file can have at most 589,822 blocks. At the default 512 bytes per
- block, that equates to about 288MB. If you need more memo space, increase the
- block size. The memo extension default is "DBT\0". Generally, it's a good
- idea to leave it at this.
-
- The maximum file sizes are enforced when adding to or reading from DBF files,
- and when inserting into or reading from index files. The default is 2047 MB
- (0x7FEFFFFF). If your file system permits 4GB files, set the values to 4095
- MB (0xFFEFFFFF).
-
- The Atomic mode flag determines how key access is handled. When bit0=0, the
- default, the key routines, NEXT_KEY_XB, PREV_KEY_XB, and GET_NEXT_XB,
- GET_PREV_XB, use the internal position of the last gotten key as their
- starting point. In multi-threaded code, it's possible that another thread has
- since accessed the same file handle and altered the last gotten key. By
- setting bit0=1, key access (next or previous) can now specify a starting point
- (typically already set up in AP.keyPtr), rather than starting at the last
- accessed key for that handle (which may have been changed by another thread).
-
- Note: This routine is not mutex-protected.
-
-
- ΓòÉΓòÉΓòÉ 9.9. SET_SYSVARS_XB ΓòÉΓòÉΓòÉ
-
- Pack: QUERYSETPACK Source Example
-
- IN OUT
- QSP.func QSP.stat
- QSP.item QSP.itemValue
- QSP.itemValue
-
- Set a BULLET system variable, returning the previous value.
-
- To use, set QSP.item to the item to set, and QSP.itemValue with the value to
- use (function's address, variable's timeout value, etc., whatever the case may
- be). On return, QSP.itemValue is the previous value that QSP.item was set to.
-
- QSP.item FuncPtr To
- 1 ASCII sort compare
- 2 NLS sort compare
- 3 16-bit signed integer
- 4 16-bit unsigned integer
- 5 32-bit signed integer
- 6 32-bit unsigned integer
- 7-9 Reserved
-
- All intrinsic sort compares (1-6) point to the same function. They cannot be
- called except by BULLET itself. They should not be overloaded with custom
- functions. If you have a custom sort-compare, use one of the custom slots.
- The integer compare routines are based on Intel byte order. For Motorola byte
- order, ASCII sort can be used for all-positive numbers, otherwise a custom
- sort-compare should be used.
-
- 10-19 Custom sort-compare functions
-
- Before creating or opening an index file with a custom sort-compare function
- (which is specified during CREATE_INDEX_XB), that function's address must
- first be sent to BULLET using this routine. Thereafter, that function must be
- available whenever that index file is accessed. See Custom Sort-Compare
- Function for creating custom sort-compare functions.
-
- To set the function pointers to the build key and expression parser routines,
- use:
-
- QSP.item FuncPtr To
- 20 Build key routine
- 21 Key expression parser routine
- 22-28 Reserved
-
- Before creating or opening an index file with a custom build key or expression
- parser routine (which is specified at any time, but must be used in a
- consistent manner), that routine's address must first be sent to BULLET using
- this routine. Thereafter, that routine should always be ready (in a callable
- state) since it may be required again. See Custom Build-Key Routine for
- creating a custom build-key routine and Custom Expression Parser Routine for
- creating a custom key expression parser.
-
- To set the BULLET system variables' values, use:
-
- QSP.item Value To
- 30 Lock file bytes timeout, in milliseconds (default=0)
- 31 Mutex semaphore request timeout, in milliseconds (default=0)
- 32 Pack buffer size, in bytes (default=0: autosize)
- 33 Reindex buffer size, in bytes (default=0: autosize)
- 34 Reindex node pack percentage, 50-100% (default=100)
- 35 Temporary file path pointer (default=NULL, where TMP= used, then .\)
- 36 Reindex tag field character to skip (default=0, no skip)
- 37 Commit each individual file during INSERT/UPDATE_XB (default=0, defer until flush)
- 38 Memo file block size (default=512 bytes; minimum is 24 bytes)
- 39 Memo file extension (default is "DBT\0")
- 40 Max data file size-1 (default=2047MB, absolute max is 4095MB)
- 41 Max index file size-1 (default=2047MB, absolute max is 4095MB)
- 42 Atomic mode (bit0=1 then atomic Next and Prev access, default=0)
-
- The timeout values determine if the kernel should wait for a pre-determined
- time before returning an error if the resource cannot be obtained. The lock
- timeout specifies how long to wait for a lock to be obtained in case some
- other process has a lock on the same resource. The mutex timeout specifies
- how long to wait for access to BULLET in case some other thread in this
- process is in BULLET. Multiple processes can access BULLET at the same time,
- but only one thread in each process can be inside BULLET at any one time.
-
- The buffer sizes, when 0, default to a minimum reasonable size. Performance
- is acceptable at these sizes. For best performance, provide as much real
- memory as possible, up to 512KB. Larger buffers can be used.
-
- The reindex node pack percentage determines how many keys are packed on a
- node. 100% forces as many keys as possible, minus 1.
-
- If the temporary file path pointer is NULL (the default), then the TMP=
- environment variable is used to locate any temporary files created by BULLET,
- or if that is not found, then the current directory is used. The pointer
- supplied, if any, should be to a string containing an existing path (drive
- should be included; a trailing '\' is optional, but recommended). See
- REINDEX_XB for size requirements.
-
- The reindex skip tag character, if encountered in the DBF record's tag field
- (the first byte of each record), causes the reindex routine to not place that
- record's key value into the index file. Also, BUILD_KEY_XB returns a warning
- if the record supplied has a matching tag character. To disable skip tag
- processing, set it to 0.
-
- Inserts and Updates, by default, do not commit each file when that pack is
- processed. Instead, it is left to the programmer to issue a FLUSH_XB to
- commit. To force a commit after each pack file is processed, set CommitAtEach
- to 1. This is not one single commit, but a commit for each file in the pack,
- after that file has been processed, but before the next file in the pack is.
- This will not prevent a roll-back should it be needed.
-
- A memo file can have at most 589,822 blocks. At the default 512 bytes per
- block, that equates to about 288MB. If you need more memo space, increase the
- block size. The memo extension default is "DBT\0". Generally, it's a good
- idea to leave it at this.
-
- The maximum file sizes are enforced when adding to or reading from DBF files,
- and when inserting into or reading from index files. The default is 2047 MB
- (0x7FEFFFFF). If your file system permits 4GB files, set the values to 4095
- MB (0xFFEFFFFF).
-
- The Atomic mode flag determines how key access is handled. When bit0=0, the
- default, the key routines, NEXT_KEY_XB, PREV_KEY_XB, and GET_NEXT_XB,
- GET_PREV_XB, use the internal position of the last gotten key as their
- starting point. In multi-threaded code, it's possible that another thread has
- since accessed the same file handle and altered the last gotten key. By
- setting bit0=1, key access (next or previous) can now specify a starting point
- (typically already set up in AP.keyPtr), rather than starting at the last
- accessed key for that handle (which may have been changed by another thread).
-
- Note: Issuing INIT_XB restores all system variables (those setable via this
- routine) and function pointers to their default values. This is done even if
- INIT_XB returns an error that BULLET has already been initialized.
-
-
- ΓòÉΓòÉΓòÉ 9.10. SET_DVMON_XB ΓòÉΓòÉΓòÉ
-
- This routine is obsolete.
-
-
- ΓòÉΓòÉΓòÉ 9.11. CREATE_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: CREATEDATAPACK Source Example
-
- IN OUT
- CDP.func CDP.stat
- CDP.filenamePtr
- CDP.noFields
- CDP.fieldListPtr
- CDP.fileID
-
- Create a new BULLET DBF data file with the name at CDP.filenamePtr, and an
- optional DBT memo file.
-
- Before using this routine, allocate an array of field descriptors of type
- FIELDDESCTYPE, one for each field in the record (number of fields as set in
- CDP.noFields). It is recommended that this allocation be zeroed before use
- since fieldnames and reserved entries must be 0-filled:
-
- FIELDDESCTYPE fieldList[12]; // 12 fields used in data record
- :
- memset(fieldList,0,sizeof(fieldList)); // init unused bytes to 0 (required)
-
- Filename
-
- The drive and path must exist if used as part of the filename. Long filenames
- may be used if supported by the file system in use. As with all text strings
- used by Bullet, the filename must end in a '\0'.
-
- Number of Fields
-
- The number of descriptors in the array, described next. Each field has a
- descriptor. The tag field is not a formal field, and so has no descriptor,
- and is not counted in the number of fields. The maximum fields is 254
- according to the DBF standard. Bullet allows 255, but 254 should be used if
- creating a standard DBF file.
-
- Field Descriptors
-
- For each field, a descriptor is used to identify and type it. These
- descriptors are assigned to an array; the pointer to that array is assigned to
- CDP.fieldListPtr. The format of the descriptor follows, with a physical
- format in DBF File Format.
-
- Γûá Fieldname
-
- 10 characters plus null byte terminator. Valid fieldname characters are ASCII
- A-Z (upper-case) and the underscore (ASCII 95). All bytes after the fieldname
- must be null bytes. E.g., if the fieldname is "LNAME", five characters, the
- following six bytes (including the 11th byte) are set to 0. The eleventh byte
- is always a null byte since 10 characters is the maximum fieldname length.
- Extended ASCII characters (above 127) should not be used.
-
- fieldList[0].fieldname = "ANYNAME"; // see memset() above
-
- Γûá Field type and size
-
- Standard Xbase field types are C, D, L, M, and N:
-
- Type Description
- C Character field, any code page character, 1 to 255 characters.
- Null bytes are not desirable except as a string terminator. There is
- no requirement that strings be terminated with a '\0'. The field data
- should be left-justified within the field, but this is not required (in
- which case use leading spaces, not 0 bytes).
-
- fieldList[0].fieldType = 'C';
- fieldList[0].fieldLen = 25; // since C type, space fill field data
- fieldList[0].fieldDC = 0;
-
- D Date field, valid ASCII digits for date, 8 characters.
- The physical format is YYYYMMDD, where YYYY is the year (1999), MM is
- the month (1-12), and DD the day (1-31). The date field is always 8
- bytes long, and is in ASCII digits ('19991231'). If no date, set to
- all spaces.
-
- fieldList[0].fieldType = 'D';
- fieldList[0].fieldLen = 8;
- fieldList[0].fieldDC = 0;
-
- L Logical field, <SPACE> Y N T F y n t f, 1 character.
- A single-byte field. When not yet initialized the value will be a
- <SPACE> (ASCII 32). This is typically displayed as a '?' to the user,
- indicating that the field has not been initialized. Initialized values
- are variations of yes, no, true, false ('Y', 'y', etc.).
-
- fieldList[0].fieldType = 'L';
- fieldList[0].fieldLen = 1;
- fieldList[0].fieldDC = 0;
-
- M Memo field, 10 ASCII digits, 10 characters.
- Field data is used as the block number of the corresponding DBT memo
- file. Each block is typically 512 bytes, with the first block (block
- #0) used as the memo file header. If no block is used in the .DBT by
- this record, the field is set to <SPACES>. The first memo block is
- stored as "0000000001". (This description is valid for dBASE IV and
- later memo files, as created and used by BULLET.) Some Xbase versions
- use field types B and G as variations of memo files. They are as M,
- but contain general data (as in anything), while memo files contain
- only text. BULLET supports any type data in its memo files, and you may
- use the CDP.fieldType of 'B' or 'G'.
-
- More than one memo field per record is permitted. For example, you may
- need a memo for the printable address, where the address is free-form
- rather than in separate fields (i.e., you have both forms), and another
- memo for general notes, and yet a third for problem reports, and so on.
- All these, and all memos for the rest of the DBF file, are stored in
- the same DBT memo file.
-
- Note: BULLET does not use the fieldType with regard to identifying
- memo field type; it is the programmer's responsibility to check the
- fieldType and act on it accordingly.
-
- fieldList[0].fieldType = 'M';
- fieldList[0].fieldLen = 10;
- fieldList[0].fieldDC = 0;
-
- N Numeric field, ASCII digits, 19 digits maximum (see below).
- All standard Xbase data is stored in ASCII form (for universal
- exchange). Numeric fields are to be right-justified, with leading
- spaces, and an aligned decimal point, if any (relative this field in
- other records). Do not end the field with a null byte.
-
- The total size of the numeric field is specified in .fieldLen, which
- includes any leading sign, the decimal point, and decimal digits to the
- right of the decimal point (if any decimal point). The maximum total
- size is 19 places. If a decimal point, then the number of digits to
- the right may be from 1 to 15 digits, but must be no more than the
- total-2.
-
- FieldLen.FieldDC Example
- 8.2 " 2345.78"
- 8.2 "12345.78"
- 8.2 "-2345.78"
- 8.1 "123456.8"
- 8.0 "12345678"
- 5.3 "2.235"
- 5.4 (not valid)
-
- fieldList[0].fieldType = 'N';
- fieldList[0].fieldLen = 8;
- fieldList[0].fieldDC = 2;
-
- Although not dBASE compatible, you may use binary fields in your data records.
- The Xbase standard always has ASCII data in the data fields, even if the field
- is numeric. For example, an 'N' type field of 8.2 (total
- length.decimal-count) is stored as an ASCII text string in the data record,
- say, a string like " 1100.55". If you want dBASE compatibility your field
- data must also be ASCII. However, if you can forgo this requirement, you can
- use binary values in the fields.
-
- To do this you must specify a field type of 'Y' (actually, anything but an
- 'N') and, if it is to be used as a key field, also set the sort function to
- the appropriate type (S16_SORT, etc.). The field length
- (fieldList[x].fieldLen) for a 'Y' field type is 2 if 16-bit, and 4 if 32-bit.
- Also possible is floating-point (with a custom sort-compare function). A
- likely field type marker for this would be 'F'. Note that both 'Y' and 'F'
- are completely non-standard Xbase types, and only your programs will
- understand them.
-
- Note: 'B' should not be used as a binary field type marker since dBASE V uses
- 'B' to signify a binary-data memo file field. Bullet makes no distinction in
- its memo file data; anything can be placed in them. Typically, your memo
- fields are marked as 'M' in Bullet, but could also be 'B' or 'G'.
-
- File ID
-
- Conventional dBASE DBF files have a CDP.fileID=3. To create a memo file (DBT,
- dBASE IV compatible), set CDP.fileID=x8B. For the DBT to be created, both
- bits 3 and 7 (0x88) must be set. The other bits may be anything, and are not
- checked.
-
- In creating your DBF files, specify CDP.fileID=3 to ensure compatibility
- across Xbase versions. If creating a non-standard DBF (e.g., non-standard
- field types, extended field lengths, etc.) it's recommended to use
- CDP.fileID=0 or CDP.fileID=1. For a standard DBF file with a memo file (dBASE
- IV or later), use CDP.fileID=0x8B (that's a B, as in bee).
-
- Generally, field data is space-filled. String terminators are allowed in
- C-haracter field types, but should not be used in other fields.
-
- Memo File Creation
-
- If bits 3 and 7 are set in CDP.fileID, a memo file is created for the DBF. The
- memo filename will be the same as the DBF name except the extension. The memo
- file is created after the DBF, with a block size of 512 bytes, and filename
- extension of ".DBT". The default block size and extension can be overridden
- (see SET_SYSVARS_XB) prior to calling this routine.
-
-
- ΓòÉΓòÉΓòÉ 9.12. OPEN_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: OPENPACK Source Example
-
- IN OUT
- OP.func OP.stat
- OP.filenamePtr OP.handle
- OP.asMode
-
- Open an existing DBF data file for use. For DBF opens, two parameters are
- specified: the filename and the access-sharing mode. The OP.xbLink parameter
- is used only for index opens, and so is not used here.
-
- The OP.asMode has optional cache mode settings. The caching modes cover
- locality, write-through, and skip cache. Locality is typically mostly random
- (RND_LOCALITY), but may be mostly sequential if the data file has been sorted
- and the index file recently reindexed and processing is mostly in-order (first
- to last, rather than random). Locality is used to tune the cache. Also,
- normally, data is written to the cache with control returning immediately to
- the program before the disk is written (an asynchronous write). To force the
- write to take place before control is returned (a synchronous write), use the
- WRITE_THROUGH mode. To skip the cache completely, use the SKIP_CACHE mode.
- This, as all OP.asMode settings, affects this file handle only.
-
- On a successful open, the file handle is returned. Use this handle for all
- further access to this file. If the DBF was created with a compatible memo
- file, it is also opened. The handle of the memo file is available via
- STAT_DATA_XB, but all access to the memo file is made with the handle of the
- memo file's master DBF (the handle returned by this routine in OP.handle).
- The memo file is opened using the same OP.asMode.
-
- Note: FoxPro DBF files with Fox memo files (FPT) use an ID of 0xFF. Bullet
- does not support FoxPro memo files, and so opening a FoxPro DBF with a Fox
- memo file returns the warning message, WRN_CANNOT_OPEN_MEMO. The DBF file is
- opened, and the warning can be ignored.
-
- Once open, you can get information on the data file by using STAT_DATA_XB.
-
- Each DBF data file opened allocates and commits at least 4K bytes for internal
- use:
-
- Number of Fields Memory
- 1 to 121 4KB
- 122 to 249 8KB
- 250 to 255 12KB
- This memory is released when you close the file with CLOSE_DATA_XB. or issue
- EXIT_XB.
-
- Note: You must open the data file before you can open or create any of its
- index files.
-
- When BULLET creates a DBF, it forces all fieldnames to upper-case (it's a DBF
- requirement) and 0-fills them as well. On data file opens (OPEN_DATA_XB), it
- also does this, and so any header copy (COPY_DATA_HEADER_XB) will have
- upper-cased fieldnames (the original file is not changed). To prevent BULLET
- from mapping the fieldnames to upper-case (NLS mapping, though fieldnames
- should be standard ASCII characters only), set bit31 of OP.asMode to 1
- (0x80000042, for example). This skips the case mapping. Zero-filling always
- takes place, and starts after the first ' \0' byte in the fieldname.
-
-
- ΓòÉΓòÉΓòÉ 9.13. CLOSE_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Close an existing data file.
-
- Closing the file updates the file header and releases the memory used by the
- file. Any associated memo file is closed, too. Any outstanding locks should
- be unlocked before calling this routine.
-
- Note: Remaining locks belonging to this handle are released by the OS upon
- the successful close.
-
-
- ΓòÉΓòÉΓòÉ 9.14. STAT_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: STATDATAPACK Source Example
-
- IN OUT
- SDP.func SDP.stat SDP.recordLength
- SDP.handle SDP.fileType SDP.xactionFlag
- SDP.flags SDP.encryptFlag
- SDP.progress SDP.herePtr
- SDP.morePtr SDP.memoHandle
- SDP.fields SDP.memoBlockSize
- SDP.asMode SDP.memoFlags
- SDP.filenamePtr SDP.memoLastRecord
- SDP.fileID SDP.memoLastSize
- SDP.lastUpdate SDP.lockCount
- SDP.records
-
- Return information BULLET has on the DBF data file specified by SDP.handle.
-
- Item Description
- stat Return code of operation
- fileType 1 for DBF
- flags Bit0=1 if file has changed since last flush (dirty)
- Bit1=1 if the file has its entire region locked (full lock)
- Bit2=1 if the file has a shared lock in use (cannot write
- to it if so)
- progress Percentage of pack operation completed, 1-99, or 0 if done
- morePtr Always 0
- fields Number of fields per record (does not included implicit tag
- field)
- asMode Access-sharing-cache mode as specified at open (excludes
- NoCaseMap bit31)
- filenamePtr Pointer to the filename as used in OPEN_DATA_XB
- fileID ID byte used when the DBF was created (the first byte of
- the file)
- lastUpdate Date of last change (binary: high word=year (1999), low
- byte=day, high byte=month)
- records Number of records in the DBF (includes any delete-tagged
- records)
- recordLength Total length of a data record, including tag field
- xactionFlag Not currently used
- encryptFlag Not currently used
- herePtr Pointer to the internal data control area for this file
- handle
- memoHandle Handle of open memo file (0 if none)
- memoBlockSize Memo file block size (512 is typical, 24 is minimum)
- memoFlags Bit0=1 dirty
- memoLastRecord Last accessed memo record (0 if none; same as 'block
- number')
- memoLastSize Size of last accessed memo record (in bytes, including 8
- bytes overhead)
- lockCount Number of full-locks in force (locked on first, unlocked on
- last)
-
- Typically, your program tracks whether a particular handle belongs to an index
- file or data file. In cases where this is not possible, call the
- STAT_HANDLE_XB. routine to determine what file type a handle is.
-
- Note: In network environments, you should have an exclusive lock on the data
- file (and implicitly, therefore, the memo file, if any) before using this
- routine to ensure that the information is current. This also applies to
- multi-process environments on a single machine. This routine is not
- mutex-protected. During the call, the file handle must not be closed by
- another thread.
-
-
- ΓòÉΓòÉΓòÉ 9.15. READ_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Reload the disk copy of the data header for the opened DBF data file handle,
- refreshing the in-memory copy. Any associated memo file is refreshed, too.
-
- Normally, this routine is not called directly but rather is done automatically
- when you full-lock the file (LOCK_XB). This routine does not refresh the
- header if the current state is dirty (SDP.flags, bit0=1); it returns an error
- if tried. Since it is recommended that a full-lock be in force before using
- this routine (shared or exclusive), and since a full-lock always reloads the
- header anyway, calling this routine should never be required. If ever there
- is a reason to use this routine without having a full-lock in force, then, of
- course, you may need to. However, it is not wise to reload the header without
- a full-lock (which locks the header). If you are using your own lock
- routines, this call will be very useful.
-
- In single-user, single-tasking systems this routine is not needed. However,
- in a multi-user or multi-tasking system it's possible, and desirable, for two
- or more programs to use the same data file. Consider this scenario: A data
- file has 100 records. Two programs access this data file, both opening it.
- Program 1 locks the file, adds a new record, then flushes and unlocks the
- file. Program 1 knows that there are now 101 records in the file. However,
- Program 2 is not aware of the changes that Program 1 made--it thinks that
- there are still 100 records in the file. This out-of-sync situation is easily
- remedied by having Program 2 reload the data header from the file on disk.
-
- How does Program 2 know that it needs to reload the header? It doesn't.
- Instead, BULLET uses a simple, yet effective, approach when dealing with this.
- When BULLET full-locks a file, BULLET automatically reloads the header by
- using this routine. When removing the full-lock, BULLET automatically flushes
- the header using FLUSH_DATA_HEADER_XB (unless the current lock is a shared
- lock (SDP.flags bit2=1)).
-
-
- ΓòÉΓòÉΓòÉ 9.16. FLUSH_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Write the in-memory copy of the data header for the opened DBF data file
- handle to disk. The actual write occurs only if the header has been changed
- (the dirty bit is set). Any associated memo file is flushed, too. This
- routine ensures that the data header on disk matches exactly the data header
- that is being maintained by BULLET.
-
- Normally, this routine is not called directly but rather is done automatically
- when you unlock the file (UNLOCK_XB). This routine does not write out the
- header if the current lock state is shared (SDP.flags, bit2=1); it returns an
- error if tried. Unlocking a full-lock performs a flush automatically, and so
- you may never need to explicitly call this routine. Also, when relocking from
- an exclusive full-lock to a shared full-lock, an automatic flush is performed.
-
- Assume the following: A data file with 100 records. Your program opens the
- data file and adds 1 record. Physically, there are 101 records on disk.
- However, the header image of the data file on disk still reads 100 records.
- This isn't a problem, BULLET uses its internal copy of the data header and the
- internal copy does read 101 records. But, if there were a system failure now,
- the image of the header would not get updated since the disk image is written
- only on a CLOSE_ or FLUSH_DATA_XB, or on EXIT_XB (and also prior to
- PACK_RECORDS_XB). After the system restarts, BULLET opens the file, reads the
- header and thinks that there are 100 records. You lost a record. Now, if
- after that record add your program issues FLUSH_DATA_HEADER_XB, the header on
- disk is refreshed with the in-memory copy, keeping the two in sync. This
- routine also updates the directory entry for the file, keeping things neat
- there (file size). Still, it doesn't come without cost: flushing takes
- additional time, therefore, you may elect to flush periodically, or whenever
- the system is idle.
-
- Note: You should have a full-lock on the file before using this routine.
-
-
- ΓòÉΓòÉΓòÉ 9.17. COPY_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: COPYPACK Source Example
-
- IN OUT
- CP.func CP.stat
- CP.handle
- CP.filenamePtr
-
- Copy the DBF file structure of an open data file to a new file.
-
- This routine makes it easy for you to duplicate the structure of an existing
- DBF file without having to specify all the information needed by
- CREATE_DATA_XB. The resultant DBF will be exactly like the source, including
- number of fields and field descriptions, and an empty memo file, if
- applicable. It contains 0 records. It may be opened as a regular Bullet data
- file.
-
- A typical use for this is to create a work file, where only a subset of
- records are required. For example: You want to process all records of those
- whose last name starts with A. Copy the header to a work file, use GET_XB
- routines to get records meeting the criterion, writing those that fit the
- criterion to the work file (using either Add/Reindex, or Insert). A new index
- can be specified, or an existing index can be copied using
- COPY_INDEX_HEADER_XB.
-
-
- ΓòÉΓòÉΓòÉ 9.18. ZAP_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Delete all records in a DBF data file.
-
- This routine is similar to COPY_DATA_HEADER_XB except for one major
- difference: All data records in the source file are physically deleted. No
- action is performed on the DBF's memo file, if any.
-
- If you have a DBF file with 100 records and use ZAP_DATA_HEADER_XB on it, all
- 100 records will be physically deleted and the file truncated as if no records
- were ever in the file. All data records are lost forever.
-
-
- ΓòÉΓòÉΓòÉ 9.19. CREATE_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: CREATEINDEXPACK Source Example
-
- IN OUT
- CIP.func CIP.stat
- CIP.filenamePtr
- CIP.keyExpPtr
- CIP.xbLink
- CIP.sortFunction
- CIP.codePage
- CIP.countryCode
- CIP.collatePtr
- CIP.nodeSize
-
- Create a new BULLET index file.
-
- Before you can create an index file, you must first have opened (and have
- created if necessary) the BULLET DBF data file that it is to index. To open
- the data file, use OPEN_DATA_XB. To create the index file, you need to
- provide the name to use, the key expression, the DBF file link handle
- (obtained from the OPEN_DATA_XB call), sort function/flags, and optionally,
- the code page, country code, and collate table. There's also a node size
- parameter. Select 512, 1024, or 2048 bytes.
-
- Note: BULLET has an optional external data mode where only indexing is done
- -- no data file link is used. In this mode, BULLET manages the index files of
- the key and key data you provide (key data is any 32-bit item, e.g., a record
- number, offset, etc.). This would be useful for indexing non-DBF files, even
- files with variable-length records.
-
- Filename
-
- The drive and path must exist if used as part of the filename. Long filenames
- may be used if supported by the file system in use.
-
- Key Expression
-
- The key expression is an ASCIIZ string composed of the elements that are to
- make up this index file's key. The key can be composed of any or all of the
- fields in the DBF data record, or sub-strings within any of those fields. Up
- to 16 component parts can be used in the expression.
-
- Two functions are supported in evaluating a key expression. These are SUBSTR()
- and UPPER():
-
- SUBSTR() extracts part of a field's data starting at a particular position for
- x number of characters.
-
- UPPER() converts all lower-case letters to their upper-case equivalent. Since
- BULLET supports NLS, UPPER() conversion is not required for proper sorting of
- mixed-case text strings.
-
- Any name used in the key expression must be a valid field name in the DBF data
- file. Below are a few sample key expressions for the given data file
- structure:
-
- Name Type Len DC
- FNAME C 25 0
- LNAME C 25 0
- SSN C 9 0
- DEPT N 5 0
-
- A few example key expression strings for this structure:
-
- keyExpression[]="LNAME";
- keyExpression[]="LNAME+FNAME";
- keyExpression[]="SUBSTR(LNAME,1,4)+SUBSTR(FNAME,1,1)+SUBSTR(SSN,6,4)";
- keyExpression[]="UPPER(LNAME+FNAME)"; // for ASCII sort function only
- keyExpression[]="DEPT+SSN";
-
- In the last example above, even though DEPT is a numeric field type (N), it
- can still be used as a component of a multi-part character key with SSN (whose
- type is set to character). This because numeric fields in dBASE DBF data
- files are ASCII digits, not binary values, and are sorted according to the
- ASCII value or NLS weight.
-
- The key expression is parsed when the index file is created (this routine) and
- also when reindexed (REINDEX_XB.). The parser() function, which parses the key
- expression, may be replaced by a programmer-supplied function if additional
- functionality is needed. See Custom Expression Parser Routine for details.
-
- DBF File Link Handle (xbLink)
-
- Since BULLET evaluates the key expression when the file is created (this
- routine) or during reindex, it must have access to the DBF file to verify that
- the key expression is valid. You must, therefore, supply the OS file handle
- of the opened DBF data file. If you later change the structure of the DBF
- data file (add new fields, remove others, etc.), you must use the reindex
- routine to re-evaluation the key expression. If the key expression is no
- longer valid after the data file changes (key field has changed names, etc.),
- then you must create a brand new index file with this routine, supplying the
- new key expression, rather than reindexing.
-
- Note: Handles 0-2 are reserved handles and should never be used for any
- BULLET routine. Also, .xbLink of -1 is reserved by BULLET to indicate an
- external data index for index create and open routines.
-
- Sort Function
-
- The sort function specifies the sort method for the index file. Essentially,
- this defines the compare function used by the access methods employed by
- BULLET when doing any type of key access (reading and writing). There are six
- intrinsic sort compare functions available, with an additional 10 sort compare
- functions that can be specified by the programmer (see Custom Sort-Compare
- Functions).
-
- While not recommended, duplicate key values are supported and managed by
- BULLET. The flag DUPS_ALLOWED is OR'ed with the sort function value to
- specify this. Generally, it is not acceptable to allow duplicate keys for an
- index; there should be one key identifying one record without any further
- investigation needed to determine if the key is indeed for that record. This
- is not possible, not consistently so, when duplicate keys exist. It is much
- simpler to define your key so that duplicates are not generated, than it is to
- deal with duplicate keys once you have them. If an attempt to insert a key
- that already exists in the index file is made, and DUPS_ALLOWED was not
- specified when the index file was created, the insert fails (either a
- STORE_KEY_XB, an INSERT_XB, or a REINDEX_XB operation), and error
- EXB_KEY_EXISTS is returned.
-
- Only data contained within a record should be used to build a key. The
- physical record number is not part of the data of a record since it can change
- at any time without you knowing about it (during a pack, for example). Do not
- use the record number in an attempt to generate unique keys. Only use what is
- available in the data record itself, so that the key can be built, or rebuilt,
- at any time.
-
- The intrinsic sort compare functions of BULLET are:
-
- ASCII_SORT 1 - ASCII (up to 16 key components)
- NLS_SORT 2 - NLS (up to 16 key components)
- S16_SORT 3 - 16-bit signed integer (single component)
- U16_SORT 4 - 16-bit unsigned integer (single component)
- S32_SORT 5 - 32-bit signed integer (single component)
- U32_SORT 6 - 32-bit unsigned integer (single component)
-
- To expand on the basic functionality provided by BULLET, you can supply your
- own parser, build, and sort compare routines, and have BULLET use them
- instead. With your own routines in place, you can have BULLET do just about
- anything with regard to the index file, including evaluating the key
- expression dynamically; using more components; allowing multi-part binary
- keys; and more.
-
- Generally, character data (type C) is left-justified, and unused space is
- padded with the SPACE character (ASCII 32). It is permissible to use C-type
- strings, or to 0-fill unused space.
-
- Numeric data (type N) is right-justified, with leading space to be padded with
- the SPACE character. It is not permissible to use 0-fill leading bytes
- (literal '0' can used, however). Since the field is right-justified, it is
- not generally desirable to terminate the field with a 0 byte, either. If a
- decimal count is specified (not 0),the decimal point location is to be the
- same for all entries in this field. The field description must match the
- actual data: If the field length and field decimal count was specified as
- 10.2 (10 total bytes, 2 digits to the right of the decimal, then the data is
- to be formatted so that '-234567.90' is the longest data that is to be entered
- in that field. All entries in all records for this field must be of the same
- format. For example, " 987654.21", or " 23.01", or " -1.99" (note the
- leading spaces). Numeric data is indexed as ASCII values (i.e., the key
- remains character digits) unless a binary sort function is specified.
-
- Using one of the binary integer sort compare functions requires the following:
-
- 1. Single component expression.
- 2. Field type must be N if the field has ASCII digits, or if the data is
- binary, then the field type must be Y (actually, anything but N).
- 3. If ASCII digits, the value must fit into the function size (-32768 to
- 32767 or 0-65535 for signed/unsigned 16-bit; 2,147,483,647 to
- -2,147,483,648 or 0-4,294,967,295 for 32-bit signed/unsigned values).
-
- Although not dBASE compatible, you may use binary fields in your data records.
- The Xbase standard always has ASCII data in the data fields, even if the field
- is numeric. For example, an 'N' type field of 8.2 (total
- length.decimal-count) is stored as an ASCII text string in the data record,
- say, a string like " 1100.55" (there is no \0 string terminator). If you want
- dBASE compatibility, your field data must be ASCII. However, if you can forgo
- this requirement, you can use binary values in the fields.
-
- To do this you must specify a field type of 'Y' (actually, anything but an
- 'N') and, if it is to be used as a key field, also set the sort function to
- the appropriate type (S16_SORT, etc.). The field length
- (fieldList[x].fieldLen) for a 'Y' field type is 2 if 16-bit, and 4 if 32-bit.
- For 64-bit integers, a custom sort-copmare function is required since there is
- no intrinsic 64-bit function available.
-
- Note: 'B' should not be used as a binary field type marker since dBASE V uses
- 'B' to signify a binary-data memo file field. Bullet makes no distinction in
- its memo file data; anything can be place in them. Typically, your memo
- fields are marked as 'M' in Bullet, but could also be 'B' or 'G'.
-
- The key expression string you specify may be up to 159 characters, and
- evaluate out to 64 bytes (62 bytes if DUPS_ALLOWED is specified). The
- expression string must be 0-terminated, as are all strings used by BULLET
- itself (filenames, etc.).
-
- National Language Support (NLS)
-
- National Language Support is available to properly sort most languages'
- alphabets. BULLET uses NLS to build the collate sequence table (sort table)
- that it uses to ensure proper sorting of mixed-case keys as well as the
- sorting of foreign language alphabets which use extended-ASCII. In order for
- BULLET to use the proper collate table, it must know what code page and
- country code to use. If not supplied, Bullet gets this information directly
- from the OS, which provides the cc/cp for the current process. If you supply
- cc/cp, the code page must be loaded or an error is returned (see OS/2's CHCP
- command). The collate table generated is made part of the index file so that
- all subsequent access to the index file maintains the original sort order,
- even if, say, the MIS shop is moved to another location/computer system using
- another country code/code page. These three items are discussed below.
-
- Code Page
-
- To use the default code page of the current process, specify a code page of 0.
- The OS is queried for the current code page and this code page is then used.
- Any valid and available code page can be specified. This is used only if a
- custom sort-compare or NLS sort is specified.
-
- Country Code
-
- To use the default country code of the current process, specify a country code
- of 0. The OS is queried for the current country code and this code is then
- used. Any valid country code can be specified. This is used only if a custom
- sort-compare or NLS sort is specified.
-
- Custom Collate Table
-
- If a null-pointer is specified, and a custom sort-compare or NLS sort is
- specified, BULLET queries the OS for the collate sequence table to use based
- on the code page and country code specified. Otherwise, the supplied table is
- used. Intrinsic sorts other than NLS use no collate table, and the country
- code or code page are not used, either.
-
- To use a sort weight table of your own choosing, supply a non-NULL pointer to
- this parameter. If non-NULL, the passed table is used for sort compares. The
- table is composed of 256 weight values, one per character. For example, table
- position 65 ('A') and table position 97 ('a') could both be weighted 65, so
- that that each are considered equal when sorted. If a custom sort-compare
- function was specified, this sort table may, or may not, be used -- it depends
- on whether the sort compare function uses the table (it's all up to the custom
- sort-compare function's logic).
-
- Typically, you set both the code page and country code = 0, and the collate
- table pointer to NULL.
-
- Node Size
-
- The index file is read and written in node-size chunks. The larger the node
- size, the more keys are read or written per chunk. Generally, a smaller node
- size offers better random key access, while a larger node size offers better
- sequential key access.
-
- Typically, an average node utilizes 66% of the node space for keys (a very
- small number may contain only a few keys, while some may be filled
- completely). In a 512-byte node file, for a key length of 8, there is room
- for (512-5)/(keylength+8) nodes, or 31 keys. Since a typical node is filled
- to 66%, that means about 20 keys per node. For a 2048-byte node file, same
- parameters, there is room for (2048-5)/(keylength+8), or 127 keys. At the
- standard 66% load, there are typically 83 keys per 2K node. That's 3 more
- keys per 2K of disk than the 512-byte node gives for 4 nodes (20 keys*4), The
- trade-off is that each node is 4 times as large, and so requires 4 times more
- searching. Actual performance differences may be minimal, or may be great.
- Run tests on expected data to determine the best for the data and access use.
-
-
- ΓòÉΓòÉΓòÉ 9.20. OPEN_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: OPENPACK Source Example
-
- IN OUT
- OP.func OP.stat
- OP.filenamePtr OP.handle
- OP.asMode
- OP.xbLink
-
- Open an existing index file for use. For index opens, three parameters are
- specified: the filename, the access-sharing mode, and the handle of the open
- DBF file that this file indexes. It is required to open the data file before
- you can open its related index file.
-
- Note: Handles 0-2 are reserved handles and should never be used for any
- BULLET routine. Also, .xbLink of -1 is reserved by BULLET to indicate an
- external data index for index create and open routines.
-
- On a successful open, the file handle is returned. Use this handle for all
- further access to this file.
-
- Once open, you can get information on the index file by using STAT_INDEX_XB.
-
- Each index file that you open allocates and commits 4K bytes for internal use.
- This memory is released when you close the file with CLOSE_INDEX_XB or issue
- EXIT_XB, or you program terminates.
-
- The OP.asMode has optional cache mode settings. The caching modes cover
- locality, write-through, and skip cache. Locality is typically mostly random
- (RND_LOCALITY), but may be mostly sequential if the data file has been sorted
- and the index file recently reindexed and processing is mostly in-order (first
- to last, rather than random). Locality is used to tune the cache. Also,
- normally, data is written to the cache with control returning immediately to
- the program before the disk is written (an asynchronous write). To force the
- write to take place before control is returned (a synchronous write), use the
- WRITE_THROUGH mode. To skip the cache completely, use the SKIP_CACHE mode.
- This, as all OP.asMode settings, affects this file handle only.
-
- BULLET has an optional external data mode where only indexing is done -- no
- data file link is used. In this mode, BULLET manages the index files of the
- key and key data you provide (key data is any 32-bit item, e.g., a record
- number, offset, etc.). This would be useful for indexing non-DBF files, even
- files with variable-length records. Only those routines that do not access
- the data file may be used (any routine using AP.recPtr, for example, could not
- be used, but NEXT_KEY_XB may).
-
-
- ΓòÉΓòÉΓòÉ 9.21. CLOSE_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Close an open index file.
-
- Closing the file updates the file header and releases the memory used by the
- file. Any outstanding locks should be unlocked before calling this routine.
-
- Note: Remaining locks belonging to this handle are released by the OS upon
- the successful close.
-
-
- ΓòÉΓòÉΓòÉ 9.22. STAT_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: STATINDEXPACK Source Example
-
- IN OUT
- SIP.func SIP.stat SIP.keyLength
- SIP.handle SIP.fileType SIP.keyRecNo
- SIP.flags SIP.keyPtr
- SIP.progress SIP.herePtr
- SIP.morePtr SIP.codePage
- SIP.xbLink SIP.countryCode
- SIP.asMode SIP.CTptr
- SIP.filenamePtr SIP.nodeSize
- SIP.fileID SIP.sortFunction
- SIP.keyExpPtr SIP.lockCount
- SIP.keys
-
- Return information BULLET has on the index file specified by SIP.handle.
-
- Item Description
- stat Return code of operation
- fileType 0 for index, IX3
- flags Bit0=1 if file has changed since last flush (dirty)
- Bit1=1 if the file has its entire region locked (full lock)
- Bit2=1 if the file has a shared full-lock in use (cannot
- write to it if so)
- progress Percentage of reindex operation completed, 1-99, or 0 if
- done
- morePtr Always 0
- xbLink handle of the open DBF file this file indexes
- asMode Access-sharing-cache mode as specified at open
- filenamePtr Pointer to the filename as used in OPEN_INDEX_XB
- fileID '31ch' (the first four bytes of the file)
- keyExpPtr Pointer to the key expression used when the index was
- created
- keys Number of keys in the index file
- keyLength Length of the key, including 2-byte enumerator if
- DUPS_ALLOWED
- keyRecNo The DBF record number that the last accessed key indexes
- keyPtr Pointer to the last accessed key (valid for keyLength
- bytes)
- herePtr Pointer to the internal data control for this file
- codePage Code page used when the index file was created (the actual
- code page)
- countryCode Country code used when the index file was created (the
- actual country code)
- CTptr Pointer to the collate sequence table used for NLS sorting
- (each index file has its own sequence table) or NULL if not
- an NLS index
- nodeSize Size of an index node, 512, 1024, or 2048 bytes, as
- specified during create
- sortFunction The index sort-compare method (low word) and the sort flags
- (high word), with sort-compare values 1-9 being intrinsic,
- and 10-19 being custom functions; the DUPS_ALLOWED flag is
- bit0 of the high word (allowed if set)
- lockCount Number of full-locks in force (locked on first, unlocked on
- last)
-
- Typically, your program tracks whether a particular handle belongs to an index
- file or a data file. In cases where this is not possible, call the
- STAT_HANDLE_XB routine to determine what file type a handle is.
-
- Note: In network environments, you should have an exclusive lock on the index
- file before using this routine to ensure that the information is current.
- This routine is not mutex-protected. During the call, the file handle must
- not be closed by another thread.
-
-
- ΓòÉΓòÉΓòÉ 9.23. READ_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Reload the disk copy of the index header for the opened index file handle,
- refreshing the in-memory copy.
-
- Normally, this routine is not called directly but rather is done automatically
- when you full-lock the file (LOCK_XB). This routine does not refresh the
- header if the current state is dirty (SIP.flags, bit0=1); it returns an error
- if tried. Since it is recommended that a full-lock be in force before using
- this routine (shared or exclusive), and since a full-lock always reloads the
- header anyway, calling this routine should never be required. If ever there
- is a reason to use this routine without having a full-lock in force, then, of
- course, you may need to. However, it is not wise to reload the header without
- a full-lock (which locks the header). If you are using your own lock
- routines, this call will be very useful.
-
- In single-user, single-tasking systems this routine is not needed. However,
- in a multi-user or multi-tasking system it's possible, and desirable, for two
- or more programs to use the same data file. Consider this scenario: An index
- file has 100 keys. Two programs access this index file, both opening it.
- Program 1 locks the file, adds a new key, then flushes and unlocks the file.
- Program 1 knows that there are now 101 keys in the file. However, Program 2
- is not aware of the changes that Program 1 made--it thinks that there are
- still 100 keys in the file. This out-of-sync situation is easily remedied by
- having Program 2 reload the index header from the file on disk.
-
- How does Program 2 know that it needs to reload the header? It doesn't.
- Instead, BULLET uses a simple, yet effective, approach when dealing with this.
- When BULLET full-locks a file, it automatically reloads the header using this
- routine. When removing the full-lock, BULLET automatically flushes the header
- using FLUSH_INDEX_HEADER_XB (unless the current lock is a shared lock
- (SIP.flags bit2=1)).
-
-
- ΓòÉΓòÉΓòÉ 9.24. FLUSH_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Write the in-memory copy of the index header for the opened index file handle
- to disk. The actual write occurs only if the header has been changed. This
- ensures that the index header on disk matches exactly the index header that is
- being maintained by BULLET.
-
- Normally, this routine is not called directly but rather is done automatically
- when you unlock the file (UNLOCK_XB). This routine does not write out the
- header if the current lock state is shared (SIP.flags, bit2=1); it returns an
- error if tried. Unlocking a full-lock performs a flush automatically, and so
- you may never need to explicitly call this routine. Also, when relocking from
- an exclusive full-lock to a shared full-lock, an automatic flush is performed.
-
- Assume the following: An index file with 100 keys. Your program opens the
- index file and adds 1 key. Physically, there are 101 keys on disk. However,
- the header image of the index file on disk still reads 100 keys. This isn't a
- problem; BULLET uses its in-memory copy of the index header and the in-memory
- copy does read 101 keys. But, if there were a system failure after the key
- add, the disk image of the header would not get updated since the disk image
- is written only on a CLOSE_ or FLUSH_INDEX_XB, or on EXIT_XB (and also prior
- to REINDEX_XB). After the system restarts, BULLET opens the file, reads the
- header and thinks that there are 100 keys. You lost a key. Now, if after
- that key was added, your program issues a FLUSH_INDEX_HEADER_XB, the header on
- disk is refreshed with the in-memory copy, keeping the two in sync. The
- routine updates the directory entry, keeping things neat there as well (file
- size). Still, it doesn't come without cost: flushing will take additional
- time, therefore, you may elect to flush periodically, or whenever the system
- is idle.
-
- Note: You should have a full-lock on the file before using this routine.
-
-
- ΓòÉΓòÉΓòÉ 9.25. COPY_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: COPYPACK Source Example
-
- IN OUT
- CP.func CP.stat
- CP.handle
- CP.filenamePtr
-
- Copy the index file structure of an open index file to another file.
-
- This routine duplicates the structure of an existing index file without having
- to re-specify the information needed by CREATE_INDEX_XB. The resultant index
- file will be exactly like the source, including sort function and key
- expression. It contains 0 keys.
-
-
- ΓòÉΓòÉΓòÉ 9.26. ZAP_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
-
- Pack: HANDLEPACK Source Example
-
- IN OUT
- HP.func HP.stat
- HP.handle
-
- Delete all keys from an index file.
-
- This routine is similar to COPY_INDEX_HEADER_XB except for one major
- difference: All keys in the source file are physically deleted.
-
- If you have an index file with 100 keys and issue ZAP_INDEX_HEADER_XB, all 100
- keys will be physically deleted and the file truncated to 0 keys. REINDEX_XB
- can be used to rebuild the index file.
-
- Note: Since BULLET reindexes in place, the use of ZAP is not typically
- needed.
-
-
- ΓòÉΓòÉΓòÉ 9.27. GET_DESCRIPTOR_XB ΓòÉΓòÉΓòÉ
-
- Pack: DESCRIPTORPACK Source Example
-
- IN OUT
- DP.func DP.stat
- DP.fieldNumber DP.fieldNumber
- -or- DP.fieldOffset
- DP.FD.fieldName DP.FD.fieldname
- DP.FD.fieldType
- DP.FD.fieldLen
- DP.FD.fieldDC
-
- Get the field descriptor information for a field by fieldname or by field
- position.
-
- To get descriptor info by fieldname, set DP.fieldNumber=0 and set the
- DP.FD.fieldname member to the fieldname string. Fieldnames must be
- 0-terminated and 0-filled, and must be upper-case, with A-Z and _ valid
- fieldname characters. If the string matches a fieldname in the DBF descriptor
- area, that field's descriptor info is returned in DP.FD, (FD is
- FIELDDESCTYPE), and its position is returned in FD.fieldNumber.
-
- To get descriptor info by field position (i.e., field number), set
- DP.fieldNumber to the field's position. The first is field #1. The "tag"
- field is not considered a field. If the position is valid (i.e., greater than
- 0 and not beyond the last field),that field's descriptor info is returned in
- DP.FD.
-
- This routine lets you work with unknown DBF files -- those created by another
- program. By reading each field descriptor, by number, from 1 to number of
- fields (SDP.noFields), you can generate a run-time layout of the DBF file.
- Alternatively, you can get input from your user for a fieldname, and locate
- the descriptor by name.
-
- Note: If you need to add or delete a field, be sure to reindex all related
- index files so that their key expressions can be re-evaluated. To do this, you
- need to create a new data file and build it as you build any other new data
- file. Then, copy record-by-record from the old DBF to the new, using the old
- record layout for reads, and the new record layout for writes. After this,
- reindex any index file related to the DBF file. The old DBF file can then be
- deleted.
-
- If non-standard fields are used (i.e., non-char structure members to match
- non-ASCII data fields in your non-standard DBF), then be aware that your
- compiler more than likely will add padding to align on member-size boundaries.
- This will result in a mis-match between your compiler structure and your DBF
- structure (as described in fieldList[]). To prevent this, place #pragma
- pack(1) / #pragma pack() around your structures that BULLET uses. Consult
- your particular compiler for alternate methods if it does not support #pragma
- pack.
-
-
- ΓòÉΓòÉΓòÉ 9.28. GET_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle *AP.recPtr
- AP.recNo
- AP.recPtr
-
- Get the physical record from the data file into a data buffer.
-
- The data buffer is typically a struct variable defined as the DBF record
- itself is defined. For example, if the DBF record has 2 fields, LNAME and
- FNAME, each 25 characters, then then variable would be typed as:
-
- struct rectype {
- CHAR tag; /* The Xbase DBF delete flag (must be included) */
- CHAR lastName[25]; /* same length as first field's descriptor fieldLen */
- CHAR firstName[25]; /* same length as second field's descriptor fieldLen */
- }; /* 51 */
- struct rectype recbuff;
-
- The first record is at AP.recNo=1. The last is SDP.records, determined by
- STAT_DATA_XB. The buffer must be at least as large as the record length
- (SDP.recordLength).
-
- This method of accessing the data file does not use any indexing. Generally,
- this access method is not used except for special purposes (sequential
- processing where order is not required). The preferred method to access the
- data is by one of the keyed GET_XB routines.
-
- If non-standard fields are used (i.e., non-char structure members to match
- non-ASCII data fields in your non-standard DBF), then be aware that your
- compiler more than likely will add padding to align on member-size boundaries.
- This will result in a mis-match between your compiler structure (rectype
- above) and your DBF structure (as described in fieldList[]). To prevent this,
- place #pragma pack(1) / #pragma pack() around your structures that BULLET
- uses. Consult your particular compiler for alternate methods if it does not
- support #pragma pack.
-
-
- ΓòÉΓòÉΓòÉ 9.29. ADD_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recPtr
-
- Append the data record in the data buffer to the end of the DBF file.
-
- This method of adding a record involves no indexing. It is typically used to
- build a data file en masse. Indexing is deferred until all records have been
- added, and then quickly indexed using REINDEX_XB.
-
- Since ADD_RECORD_XB is extremely fast, if you have several thousand data
- records to be added at once, appending records with this routine and
- reindexing when all have been added using REINDEX_XB is often faster than
- using INSERT_XB for each record to add.
-
- The record number assigned to the record appended is determined by BULLET, and
- that record number is returned in AP.recNo.
-
- If non-standard fields are used (i.e., non-char structure members to match
- non-ASCII data fields in your non-standard DBF), then be aware that your
- compiler more than likely will adding padding to align on member-size
- boundaries. This will result in a mis-match between your compiler structure
- and your DBF structure (as described in fieldList[]). To prevent this, place
- #pragma pack(1) / #pragma pack() around your structures that BULLET uses.
- Consult your particular compiler for alternate methods if it does not support
- #pragma pack.
-
-
- ΓòÉΓòÉΓòÉ 9.30. UPDATE_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle
- AP.recNo
- AP.recPtr
-
- Write the updated data record to the physical record number.
-
- This method of updating a data record must not be used if any field being used
- as a key field (i.e., part of the key expression) is changed.
-
- This method of updating a record is very fast if you know that that update is
- not going to alter any field used as a key in any index file that uses it. You
- must, of course, first get the data record into the record buffer. You can
- then change it, and write the update out to disk using this routine.
-
- If you need to change a field that is used as a key field, or part of one
- (e.g., SUBSTR()), use the UPDATE_XB routine.
-
- If you plan on reindexing with REINDEX_XB immediately after using this
- routine, you may elect to update the data file using this method even if
- changing any field used as a key, rather than UPDATE_XB. This since UPDATE_XB
- is very disk intensive. However, if transaction support is needed (i.e.,
- updates are dependent on other updates), then UPDATE_XB should be used.
-
-
- ΓòÉΓòÉΓòÉ 9.31. DELETE_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle
- AP.recNo
-
- Tag the record at the physical record number as being deleted.
-
- This does not tag any in-memory copies of the record so be sure to mark any
- such copies as being deleted yourself.
-
- The first byte of every DBF record is reserved for the tag field. This tag is
- a space (ASCII 32) if the record is normal, or a * (ASCII 42) if it's marked
- as being deleted. This delete tag is a reserved field in the DBF record and
- as such is not defined as a formal field with a descriptor. Make sure that
- you define your in-memory buffers to reserve the first byte for the delete
- tag.
-
- The Xbase DBF standard doesn't physically remove records marked as deleted
- from the data file. It doesn't mark them as available/reusable either. To
- physically remove records marked as deleted use PACK_RECORDS_XB
-
- Records can be temporarily marked as deleted during processing and then
- recalled to normal status when completed, useful for flagging a record as
- having been processed (for example, mass updating using UPDATE_XB). The
- GET_XB routines return the record number associated with a key (in AP.recNo),
- and that record number can be used for this routine.
-
- While the DELETE_RECORD_XB and UNDELETE_RECORD_XB routines provided in BULLET
- use the * and SPACE characters only, you can use whatever character you want
- in the tag field when you fill your record buffer structure's data. Normally,
- you set the tag field to SPACE (x.tag = ' ';), but, for example, if you want
- to implement your own, program-level locking you can use the tag field as a
- marker to indicate the record is locked (by using an 'L' character, or ID with
- bit7=1, or whatever you can think of) and use the very fast UPDATE_RECORD_XB
- to set it. Another possibility is set to aside a field to be used as this,
- say, along with the user ID of the lock owner.
-
- The SKIP_TAG_SELECT item in SET_SYSVARS_XB can be set to have the REINDEX_XB
- routine not place a key value into the index file if a record has a matching
- tag field. This may be useful if you want to, say, generate an ad hoc index
- for only undeleted records.
-
-
- ΓòÉΓòÉΓòÉ 9.32. UNDELETE_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle
- AP.recNo
-
- Tag the record at the physical record number as being normal (not deleted).
-
- This does not tag any in-memory copies of the record so be sure to mark any
- such copies as being normal.
-
- This routine removes the * character, as put there by DELETE_RECORD_XB, in the
- tag field and replaces it with a SPACE. The tag field is always overwritten
- with a SPACE, regardless of what it was.
-
-
- ΓòÉΓòÉΓòÉ 9.33. PACK_RECORDS_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle
-
- Rebuild the open DBF file by physically removing all records marked as
- deleted.
-
- Packing occurs in place using the existing file. It is recommended that you
- use BACKUP_FILE_XB to copy the current DBF file before using this routine in
- case of a failure during the pack process.
-
- The newly packed file is truncated to reflect the current, actual size. All
- records with the tag field set to * are removed from the file.
-
- If there are index files for this DBF file, they must be reindexed after the
- pack process by using REINDEX_XB.
-
- Memo files are not affected by this routine. Before packing, it is
- recommended that you traverse the data file to be packed, and for records that
- are to be deleted, check to see if there is a memo record. If there is, delete
- the memo. Do this for each such occurrence. This way, orphaned memo records
- will not take up permanent space in the memo file.
-
-
- ΓòÉΓòÉΓòÉ 9.34. DEBUMP_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle
- AP.recNo
-
- Remove the record identified by AP.recNo from the data file if and only if the
- record is the last in the file.
-
- Unlike DELETE_RECORD_XB, this routine physically removes a data record from
- the DBF file, provided that the record to delete is the last. STAT_DATA_XB can
- be used to identify the last record number (SDP.records is the last). This,
- when used after deleting any and all keys in all index files referencing this
- record (see DELETE_KEY_XB), is useful if you are managing a transaction log
- and need to back out changes made, beyond what BULLET performs.
-
- If the record is not the last, alternate methods must be used. The simplest,
- and often equally as good as physically deleting the record, is to just mark
- the record as deleted using DELETE_RECORD_XB and let it remain in the file
- until the next PACK_RECORDS_XB. Another option is to overwrite the record's
- data with SPACES, or other appropriate field data (such as HIGH-VALUES, and
- use UPDATE_XB), if necessary. This routine is the only method available to
- physically remove a record from the file, short of using PACK_RECORDS_XB.
- Removing a record with active keys referencing that record will result in an
- access error (ERR_UNEXPECTED_EOF) when accessing that key with GET_XB
- routines, or will generate stale results. Remove any keys that reference this
- record before deleting it.
-
-
- ΓòÉΓòÉΓòÉ 9.35. GET_MEMO_SIZE_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMODATAPACK Source Example
-
- IN OUT
- MDP.func MDP.stat
- MDP.dbfHandle MDP.memoBytes
- MDP.memoNo
-
- Get the number of bytes used by the memo at MDP.memoNo.
-
- Memo file allocation is made in blocks, typically of 512 bytes each.
- Therefore, a memo of 10 bytes uses 1 allocation block, as would a 500-byte
- memo. This size is stored with each memo record, and can be retrieved.
- Before accessing a memo record, it's a good idea to retrieve the current size
- of the memo so you know how large a buffer you may need if you intend to read
- it all in, at one time, or even to just know how much to read, in total,
- reading parts of it at a time.
-
- The first memo is at MDP.memoNo=1. The last memo number cannot be easily
- determined, but generally this does not need to be known. The memo number
- identifying the memo's location is stored in the memo field area of the DBF
- record. It is stored as a text string (e.g., "0000000001"). This number is
- the physical block number at which the memo starts. Memos are always stored
- in consecutive blocks, if more than a single block is needed. For example, a
- memo of 513 bytes uses two blocks, say, #1 and #2. The next memo added would
- use memo #3 (if #3 is available), rather than #2 since #2 was used by the
- first memo. Memo numbers may be reassigned (see UPDATE_MEMO_XB). The highest
- possible memo number is 589,822 (0x8FFFE). With the standard 512-byte block
- size, this allows a memo file to be up to 288MB. If more memo data space is
- needed, use a larger block size (e.g., 2KB block size allows over 1GB per memo
- file).
-
- Notice For All Memo Routines
-
- In multitasking environments you should have a full-lock on the DBF file that
- owns this memo file, or at least a record lock on the record that owns the
- memo number. In BULLET, locking is not performed on the memo file. Instead,
- the lock is implied when the lock is made on the DBF file. This because a
- memo file is for one DBF file alone, and so if you have a lock on the DBF
- before accessing the memo file (for whatever reason), then no other process
- may lock the DBF and also access the memo.
-
- This works only if you restrict your access to the memo file if you have a
- lock on the DBF master file (the DBF that this DBT memo file belongs to) or on
- the DBF record. For this routine, which only requires access to this memo
- record, a record lock is sufficient since no writing is performed. Further, a
- shared lock is all that is required. This because all that is required to
- keep from stepping on other process's toes is that it be known that the
- current memo header info (for this memo record), as known to this process, is
- the current state of this memo. In other words, it must be true that the memo
- file state on disk exactly matches the memo file state in memory. With a lock
- in place, no other process may gain write access to change this memo, "out
- from under you". A shared lock does allow the other process to read this
- memo, and that may be used if no writing is needed.
-
- Each memo routine following states its lock requirements (exclusive full lock,
- shared full lock, exclusive record lock, or shared record lock).
-
-
- ΓòÉΓòÉΓòÉ 9.36. GET_MEMO_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMODATAPACK Source Example
-
- IN OUT
- MDP.func MDP.stat
- MDP.dbfHandle *MDP.memoPtr
- MDP.memoNo MDP.memoBytes
- MDP.memoPtr
- MDP.memoOffset
- MDP.memoBytes
-
- Read the specified number of bytes of the memo, starting at the offset, into
- the buffer. The actual number of bytes read is returned.
-
- Use GET_MEMO_SIZE_XB to determine that total number of bytes you may need to
- read. With that, you can allocate a buffer of that size to read the entire
- memo into. Or, you can read chunks of the memo at a time, up to the number of
- bytes in the memo.
-
- The number of bytes actually read (and stored starting at MDP.memoPtr) is
- returned in MDP.memoBytes (overwriting the value you placed there). If the
- number of bytes requested is not the same as the number of bytes returned, you
- attempted to read beyond the end of the memo. BULLET does not return an error
- if you try this, which is SOP for file reads, so check the two if you need to
- verify this. An error is returned, however, if you attempt to read at a
- starting offset beyond the end of the actual memo data (i.e., MDP.memoOffset >
- memo's data size).
-
- It's recommended that a lock be in force on either the DBF (full-lock) or on
- the record that this memo belongs to. A shared lock is okay since no writing
- is done.
-
-
- ΓòÉΓòÉΓòÉ 9.37. ADD_MEMO_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMODATAPACK Source Example
-
- IN OUT
- MDP.func MDP.stat
- MDP.dbfHandle MDP.memoNo
- MDP.memoPtr
- MDP.memoBytes
-
- Add the data from the buffer for the specified bytes to a new memo. The memo
- number used is returned.
-
- Any data can be stored in a memo. The memo number returned can be any value;
- it can even be less than the previous add's memo number. The reason for this
- is that an avail-list is kept for the memo file, and any deleted or otherwise
- freed blocks become available for re-use. The memo is stored in the first
- contiguous group of free blocks large enough to satisfy the request. For
- example, if MDP.memoBytes is from 1 to (blockSize-8) bytes, the first
- available block is used. If the size needed is greater than 1 block, then the
- avail-list is walked and the first contiguous group large enough to satisfy
- the request is used. If none of the avail-list groups is large enough,
- ultimately, the new memo data is appended to the end of the file. This is
- also done if there are no avail-list items at all, such as in a memo file that
- has never had deletes or updates.
-
- The returned memo is a binary block number (ULONG). This value should be
- converted into an ASCII string (sprintf can be used) and stored in the DBF
- data record, in the memo field. The string should be of the form,
- '0000000001' (for MDP.memoNo=1), with leading zeros. This data record should
- then be written to disk using UPDATE_RECORD_XB. Since BULLET can be used in
- non-standard Xbase mode, where binary field values can be used, you can omit
- the conversion from binary to ASCII if a standard DBF is not required.
- Likewise, when accessing a memo, the conversion from ASCII to binary would not
- be required.
-
- It's recommended that a lock be in force on the DBF (full-lock). A shared
- lock may not be used since writing to the memo file, and the DBF record, is
- required. A full lock is required since the memo file header is read and
- written.
-
-
- ΓòÉΓòÉΓòÉ 9.38. UPDATE_MEMO_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMODATAPACK Source Example
-
- IN OUT
- MDP.func MDP.stat
- MDP.dbfHandle MDP.memoNo
- MDP.memoNo
- MDP.memoPtr
- MDP.memoOffset
- MDP.memoBytes
-
- Update an existing memo. The update can overwrite current data, append new
- data extending the current size, or it can shrink the current size.
-
- Appending data so that the memo is extended may result in a new memo number
- returned. The original memo blocks are made available for reuse (deleted).
- Shrinking will not change the memo number, but unused blocks from the shrink
- are made available for reuse.
-
- If you want to change anything in the memo at MDP.memoNo, locate its position
- within the memo with MDP.memoOffset and set the size in MDP.memoBytes. The
- first data byte of a memo is located at MDP.offset=0. There are 8 bytes of
- overhead per memo record (any number of blocks still has only the 8 bytes of
- overhead), but these are transparent to any memo access you do. The bytes at
- MDP.memoPtr overwrite the current memo data at the position specified. For
- example, if you want to change the first 5 bytes of the first memo, set
- MDP.memoNo=1, MDP.memoPtr=yourNewData, MDP.memoOffset=0, and MDP.memoBytes=5.
- On return, MDP.memoNo is going to be the same as it was before the update,
- since you are not extending the memo size in this example. Nothing further
- needs to be done; the memo is updated.
-
- If you want to add new memo data to an existing memo at MDP.memoNo, such as
- adding another line item, or problem report paragraph, etc., set
- MDP.memoOffset=theCurrentMemoSize (this locates to the end of the current memo
- data), MDP.memoBytes=bytesYouWantToAppend, and MDP.memoPtr=yourDataToAppend.
- If the old data size plus your newly added data still fits inside the last
- memo block previously used, MDP.memoNo is returned the same as it was on
- entry. However, if the new data requires that more blocks be allocated, the
- entire memo is relocated to the next contiguous block group that is large
- enough to store the data. That new block number is returned in MDP.memoNo,
- and the old block number and all its blocks are placed on the top of the
- avail-list.
-
- If you want to shrink the size as reported by GET_MEMO_SIZE_XB from an
- existing memo at MDP.memoNo, set MDP.memoBytes=newSizeYouWant, and
- MDP.memoPtr=NULL. This means that you should have, before making this shrink
- call, updated the memo data that occurs within this new size to be the data
- size you want to be in the memo. For example, if you have 10 line items, say,
- each 60 bytes long, and want to remove line item #5, you could do it by
- reading all 10 line items to memory, moving line items #6 to 10 down one (so
- they are now line items #5 to 9, effectively removing old line item #5), and
- update the memo (by using memoOffset=0 and memoBytes=9*60). After this,
- though, you still have 10*60 bytes as the memo size (old line item #10 is now
- at #9 and still at #10). Since you want the size to reflect the real data in
- the memo, set MDP.memoBytes=90, MDP.memoPtr=NULL, and update this memo number.
- Only the memo's size is affected by this particular update. The size
- specified must be smaller than the original size, or an error is returned.
-
- It's recommended that a lock be in force on the DBF (full-lock). A record
- lock should not be used if the update may result in blocks being moved, or the
- memo being shrunk by a full block or more. A shared lock may not be used
- since writing to the memo file, and to the DBF record if MDP.memoNo is new, is
- required.
-
-
- ΓòÉΓòÉΓòÉ 9.39. DELETE_MEMO_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMODATAPACK Source Example
-
- IN OUT
- MDP.func MDP.stat
- MDP.dbfHandle
- MDP.memoNo
-
- The memo and all its blocks are made available for reuse.
-
- Before using PACK_RECORDS_XB, you should run through all DBF records and check
- for those records that are deleted (record.tag='*') to be sure that any memo
- belong to those records are deleted from the memo file. If this is not done,
- orphaned memo records -- those that do not have a DBF record memo field
- pointing to it, may be left in the memo file (forever!).
-
- After deleting a memo record, update the DBF record's memo field by writing
- <SPACES> (ASCII 32) to the memo field member. Update this to disk with
- UPDATE_RECORD_XB as soon as possible (and before unlocking). A memo field
- with no current memo record is indicated by spaces ('0000000000' should not be
- used).
-
- It's recommended that a lock be in force on the DBF (full-lock). Neither a
- record lock nor a shared lock may be used since writing to the memo file
- header and the DBF record is required.
-
-
- ΓòÉΓòÉΓòÉ 9.40. MEMO_BYPASS_XB ΓòÉΓòÉΓòÉ
-
- Pack: MEMODATAPACK Source Example
-
- IN OUT
- MDP.func MDP.stat
- MDP.dbfHandle
- MDP.memoBypass
-
- Memo files are created, opened, closed, and flushed/reloaded by their
- corresponding DBF data file action. To perform these tasks asynchronously,
- this routine is used. Bypass routines are:
-
- MDP.memoBypass Value
- BYPASS_CREATE_MEMO 1
- BYPASS_OPEN_MEMO 2
- BYPASS_CLOSE_MEMO 3
- BYPASS_READ_MEMO_HEADER 4
- BYPASS_FLUSH_MEMO_HEADER 5
-
- All bypass routines require the handle of the DBF file that this memo is for.
- Nothing is returned here, except the result code. The memo handle from the
- open is stored internally, but is available by using STAT_DATA_XB and checking
- SDP.memoHandle. However, none of the BULLET memo routines use the memo handle
- directly; all access to the memo file is through the master DBF file handle.
-
- No data is required for input other than the DBF handle and memo bypass
- routine to perform (see table above). All required info is obtained from the
- DBF file's information. You may use an alternate block size, as set via
- SET_SYSVARS_XB.
-
- Generally, there is no need to call these routines using this bypass. However,
- if you need to create a memo file anew (say, after the initial DBF was
- created), and then open it, using these routines is the easiest way to
- proceed.
-
- Note: When creating a memo via the bypass method, the file ID is altered to
- indicate that the DBF has a DBT memo file. The file ID is the first byte of
- the DBF file. The ID is changed by OR'ing 0x88h with the current file ID
- value. The next flush or close updates the disk image of the DBF with the new
- file ID. The next DBF open, then, also opens the DBT memo file created here.
- Be sure to always keep the DBT and DBF pairs in the same directory, if moved.
-
- Since the DBF file is already open (and must be to use any of these routines),
- you must use the open bypass routine to open the memo if you plan on using it.
- Either that, or close the DBF after you've create the memo file, and simply
- re-open the DBF, which also, now, opens the DBT memo file.
-
- The other available bypass routines: close, read, and flush, typically will
- not be used from this bypass routine. These operations are done automatically
- when their corresponding DBF action is performed, and have little
- functionality used on their own.
-
- Before using BYPASS_READ_MEMO_HEADER or BYPASS_FLUSH_MEMO_HEADER, it's
- recommended that a lock be in force on the DBF (full-lock). A shared lock can
- be used for BYPASS_READ_MEMO_HEADER, but it must be a full lock.
-
-
- ΓòÉΓòÉΓòÉ 9.41. FIRST_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr *AP.keyPtr
-
- Retrieve the first key in index order from the index file.
-
- This routine does not access the DBF file and so does not retrieve the data
- record. What it does do is locate the first logical key of the index file,
- returning it, and also returning the record number within the DBF that the key
- indexes.
-
- To retrieve the data record you can use the GET_RECORD_XB routine. The
- preferred method, however, is to use GET_FIRST_XB, which combines these
- operations.
-
- The key returned includes an enumerator if the index file allows duplicate
- keys.
-
- This routine is typically used to position the index file to the first key so
- as to allow forward in-order access to the keys by using NEXT_KEY_XB.
-
- If an external data file was specified in CREATE_INDEX_XB, the record number
- returned by this routine does not refer to a DBF record, but rather is the
- value supplied when the key was stored. This permits index access to your
- data files (data files which are not maintained by BULLET, but by you).
-
-
- ΓòÉΓòÉΓòÉ 9.42. EQUAL_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr
-
- Search for the exact key in the index file.
-
- This routine does not access the DBF file and so does not retrieve the data
- record. What it does do is search for the key in the index, and if found,
- returns the record number within the DBF that the key indexes. The key must
- be an exact match, including the enumerator word if the index file is using
- non-unique keys.
-
- To retrieve the data record you can use the GET_RECORD_XB routine. The
- preferred method, however, is to use GET_EQUAL_XB, which combines these
- operations.
-
- This routine returns no key in *keyPtr since, by definition, you already have
- the key in the key buffer if this routine succeeds.
-
- This routine finds only an exact match to the specified key (including the
- enumerator if applicable). However, even if the exact key is not found, the
- index file is positioned so that a NEXT_KEY_XB retrieves the key that would
- have followed the unmatched specified key. For example, if the key to match
- were "KINGS" (a partial key, say, with \0\0 after the S), EQUAL_KEY_XB would
- return a key not found error (since no exact match was found). If you were to
- now do a NEXT_KEY_XB, the next key logically ordered after "KINGS" would be
- returned. Let's say "KINGSTON" was the next. That key value, including
- enumerator if any, and the key's record number is returned from the
- NEXT_KEY_XB call. This technique lets you position anywhere in the index file
- to narrow down any manual searches (for instance, if you're looking for a key
- but aren't sure of the exact spelling).
-
- Note: When using the partial key technique shown above, be sure to set the
- unspecified characters of the key to \0, or at least the two bytes immediately
- following your search criterion. This for both unique and non-unique index
- files. This is to ensure that the key located is the first key matching your
- search criterion.
-
-
- ΓòÉΓòÉΓòÉ 9.43. NEXT_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr *AP.keyPtr
-
- Retrieve the next key in index order from the index file.
-
- This routine does not access the DBF file and so does not retrieve the data
- record. What it does do is retrieve the next key of the index, returning it,
- and also returning the record number within the DBF that the key indexes.
-
- To retrieve the data record you can use the GET_RECORD_XB routine. The
- preferred method, however, is to use GET_NEXT_XB, which combines these
- operations.
-
- The key returned includes an enumerator if the index file allows duplicates.
-
- This routine is typically called after the index file has first been
- positioned to a known key using either FIRST_KEY_XB or EQUAL_KEY_XB, or after
- a previous NEXT_KEY_XB or even PREV_KEY_XB. What it basically does is get the
- key following the current key, and then make that key the new current key. ________________________________________________
-
- If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is
- based on a given starting point. This simplifies index access in
- multi-threaded code, where another thread may have altered the last key
- accessed in the index file. This mode lets you set a starting point for the
- operation by supplying in AP.keyPtr the key value to start at.
-
- For example, say you use GET_FIRST_XB. On return, AP.keyPtr has the the very
- first key. Say elsewhere in your multi-threaded program, another operation
- accesses that same index file handle, and performs some other access, where
- the last accessed key is no longer the same (i.e., not the first key). Your
- first thread is expecting that a GET_NEXT_XB would get the second key,
- however, it very likely won't since the second thread has altered the last
- accessed key for that file handle. By using the atomic mode for key access,
- your first thread, which has the first key value in its AP.keyPtr, can do a
- call to GET_NEXT_XB and get expected results, since the NEXT operation first
- positions to the value in AP.keyPtr and then follows up with a GET_NEXT
- operation. This is performed within the Bullet kernel, and so won't be
- interrupted by another thread (i.e., it is an atomic operation). For this to
- work, you must ensure that the AP.keyPtr value is set to the value of the last
- accessed key. This will always be the case unless uninitialized, or you are
- using global variables for your threads' AP (AccessPack). On return from the
- operation, AP.keyPtr will once again be set up for another atomic operation.
-
- Note: You must supply a valid key value for this atomic access mode.
- AP.keyPtr must be at least as large as the key length in all cases, and is to
- have the starting point for the operation (i.e., the last accessed key). You
- may, alternatively, set the first byte of the key buffer to 0 (but not
- AP.keyPtr itself to NULL). This disables atomic mode for that access, and
- reverts to the internally-stored last key accessed as the starting point.
-
-
- ΓòÉΓòÉΓòÉ 9.44. PREV_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr *AP.keyPtr
-
- Retrieve the previous key in index order from the index file.
-
- This routine does not access the DBF file and so does not retrieve the data
- record. What it does do is retrieve the previous key of the index, returning
- it and also returning the record number within the DBF that the key indexes.
-
- To retrieve the data record you can use the GET_RECORD_XB routine. The
- preferred method, however, is to use GET_PREV_XB, which combines these
- operations.
-
- The key returned includes an enumerator if the index file allows duplicates.
-
- This routine is typically called after the index file has first been
- positioned to a known key using either LAST_KEY_XB or EQUAL_KEY_XB, or after a
- previous PREV_KEY_XB or even NEXT_KEY_XB. What it basically does is to get
- the key previous the current key, and then make that key the new current key. ________________________________________________
-
- If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is
- based on a given starting point. This simplifies index access in
- multi-threaded code, where another thread may have altered the last key
- accessed in the index file. This mode lets you set a starting point for the
- operation by supplying in AP.keyPtr the key value to start at.
-
- For example, say you use GET_FIRST_XB. On return, AP.keyPtr has the the very
- first key. Say elsewhere in your multi-threaded program, another operation
- accesses that same index file handle, and performs some other access, where
- the last accessed key is no longer the same (i.e., not the first key). Your
- first thread is expecting that a GET_NEXT_XB would get the second key,
- however, it very likely won't since the second thread has altered the last
- accessed key for that file handle. By using the atomic mode for key access,
- your first thread, which has the first key value in its AP.keyPtr, can do a
- call to GET_NEXT_XB and get expected results, since the NEXT operation first
- positions to the value in AP.keyPtr and then follows up with a GET_NEXT
- operation. This is performed within the Bullet kernel, and so won't be
- interrupted by another thread (i.e., it is an atomic operation). For this to
- work, you must ensure that the AP.keyPtr value is set to the value of the last
- accessed key. This will always be the case unless uninitialized, or you are
- using global variables for your threads' AP (AccessPack). On return from the
- operation, AP.keyPtr will once again be set up for another atomic operation.
-
- Note: You must supply a valid key value for this atomic access mode.
- AP.keyPtr must be at least as large as the key length in all cases, and is to
- have the starting point for the operation (i.e., the last accessed key). You
- may, alternatively, set the first byte of the key buffer to 0 (but not
- AP.keyPtr itself to NULL). This disables atomic mode for that access, and
- reverts to the internally-stored last key accessed as the starting point.
-
-
- ΓòÉΓòÉΓòÉ 9.45. LAST_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr *AP.keyPtr
-
- Retrieve the last key in index order from the index file.
-
- This routine does not access the DBF file and so does not retrieve the data
- record. What it does do is locate the last key of the index, returning it,
- and also returning the record number within the DBF that the key indexes.
-
- To retrieve the data record you can use the GET_RECORD_XB routine. The
- preferred method, however, is to use GET_LAST_XB, which combines these
- operations.
-
- The key returned includes an enumerator if the index file allows duplicates.
-
- This routine is typically used to position the index file to the last key so
- as to allow reverse in-order access to the keys by using PREV_KEY_XB.
-
-
- ΓòÉΓòÉΓòÉ 9.46. STORE_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle
- AP.recNo
- AP.keyPtr
-
- Insert the key into the index file in proper key order.
-
- This routine does not add the data record to the DBF file. It only inserts
- the key and record number into the index file. Use INSERT_XB instead.
-
- To do a complete data record and key insert, use ADD_RECORD_XB to add the data
- record to the DBF, BUILD_KEY_XB to construct the key, then STORE_KEY_XB to
- insert the key and record number information into the index file. If that key
- already exists and the file allows duplicate keys, attach the proper
- enumerator word and retry STORE_KEY_XB. INSERT_XB does this automatically.
-
-
- ΓòÉΓòÉΓòÉ 9.47. DELETE_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr
-
- Physically remove the specified key from the index file.
-
- This routine requires an exact key match for all bytes of the key, including
- the enumerator word if duplicate keys are allowed.
-
- Typically, this routine would seldom be used since deleted DBF data records
- are only physically deleted during a PACK_RECORDS_XB operation, after which a
- REINDEX_XB is done. It is useful if you are managing a transaction log and
- need to back out changes made, beyond what BULLET performs. Also see
- DEBUMP_RECORD_XB. If you have non-unique keys (where DUPS_ALLOWED is true),
- you may have several keys that match your criterion, and only differ in their
- enumerator. To identify which key, then, goes to a particular DBF record,
- compare that key's AP.recNo with the number of your DBF record. If they are
- the same, then this key belongs to that record. Use either the KEY_XB or the
- GET_XB routines, then, before using this routine. In other words, use this
- routine only after you have identified exactly the key to delete, and for the
- exact data record. Once you have the record number, you can locate its key by
- using GET_KEY_FOR_RECORD_XB.
-
-
- ΓòÉΓòÉΓòÉ 9.48. BUILD_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle *AP.keyPtr
- AP.recPtr
- AP.keyPtr
-
- Build the key for the specified data record based on the key expression for
- the index file. If the index file allows duplicate keys, a 0-value enumerator
- is attached.
-
- This routine, like most of the mid-level routines, typically would not be used
- since the high-level access routines take care of this detail automatically.
- If used, it normally would be used prior to STORE_KEY_XB.
-
- This routine can be replaced. See Custom Build-Key Routine.
-
- Note: If DUPS_ALLOWED, this routine always sets the enumerator to \0\0.
- Enumerator management, which is used to guarantee a unique key, is performed
- only when the INSERT_XB routine is used.
-
-
- ΓòÉΓòÉΓòÉ 9.49. GET_CURRENT_KEY_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr *AP.keyPtr
-
- Retrieve the current key value for the specified index file handle and also
- the data record number that it indexes. The key value includes the enumerator
- if applicable.
-
- This routine is useful in that it retrieves, on demand, the actual key value
- of the last accessed key in the index file (and the data record number
- associated with that key). STAT_INDEX_XB returns this information, too.
-
-
- ΓòÉΓòÉΓòÉ 9.50. GET_KEY_FOR_RECORD_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle *AP.keyPtr
- AP.recNo
- AP.recPtr
- AP.keyPtr
-
- Retrieve the key for the record/record number pair.
-
- This routine would typically be used prior to using DELETE_KEY_XB and
- DEBUMP_RECORD_XB. The key returned includes the enumerator if applicable.
- This routine sifts through any duplicate keys (if DUPS_ALLOWED) for the key
- that matches the record/record number pair, and so requires both the actual
- data record along with its physical record number (even if dups are not
- allowed).
-
- Typically this routine is extraneous; the key is available with a GET_XB
- routine and so can be deleted from the information provided through normal
- access.
-
- This routine builds a key based on the supplied record at AP.recPtr and
- searches the index for that key proper. If found, and if DUPS_ALLOWED, each
- key matching the key proper has its record number compared to the record
- number in AP.recNo. If that matches, too, then that is the exact key being
- sought.
-
-
- ΓòÉΓòÉΓòÉ 9.51. GET_FIRST_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recPtr *AP.recPtr
- AP.keyPtr *AP.keyPtr
-
- Retrieve the first indexed key and its data record.
-
- The key returned includes an enumerator if the index file uses non-unique keys
- (DUPS_ALLOWED).
-
- This routine is typically used to process a database in index order starting
- at the first ordered key (and its data record). After processing this first
- entry, subsequent in-order access of the database is achieved by using
- GET_NEXT_XB, until the end of the database is reached, at which point an error
- is returned.
-
- This routine, like all the high-level GET_XB routines, fills in the AP.recNo
- of the record accessed. In this case, it fills AP.recNo with the record
- number pointed to by the first key. Since this is done upon each GET_XB
- access, the AP pack is primed for an UPDATE_XB
-
-
- ΓòÉΓòÉΓòÉ 9.52. GET_EQUAL_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recPtr *AP.recPtr
- AP.keyPtr
-
- Search for the exact key in the index file and return its data record.
-
- This routine finds only an exact match to the specified key (including the
- enumerator if applicable). However, even if the exact key is not found in the
- index file, the index file is positioned so that the next GET_NEXT_XB
- retrieves the key that would have followed the unmatched specified key. In
- this manner, a GET_GREATER_THAN_OR_EQUAL is easily performed. See also
- EQUAL_KEY_XB.
-
- This routine, like all the high-level GET_XB routines, fills in the AP.recNo
- of the record accessed. In this case, it fills AP.recNo with the record
- number pointed to by the matching key. Since this is done upon each GET_XB
- access, the AP pack is primed for an UPDATE_XB
-
- Note: When using the partial key technique as described in EQUAL_KEY_XB, be
- sure to set the unspecified characters of the key to \0, or at least the two
- bytes immediately following your search criterion. This for both unique and
- non-unique index files.
-
-
- ΓòÉΓòÉΓòÉ 9.53. GET_NEXT_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recPtr *AP.recPtr
- AP.keyPtr *AP.keyPtr
-
- Retrieve the next indexed key and its data record.
-
- The key returned includes an enumerator if the index file uses non-unique keys
- (DUPS_ALLOWED).
-
- This routine is typically called after the index file has first been
- positioned to a known key using either GET_FIRST_XB or GET_EQUAL_XB, or after
- a previous GET_NEXT_XB or even GET_PREV_XB. What it basically does is get the
- key and data record following the current key, and then makes that key the new
- current key.
-
- This routine, like all the high-level GET_XB routines, fills in the AP.recNo
- of the record accessed. In this case, it fills AP.recNo with the record
- number pointed to by the next key (now the current key). Since this is done
- upon each GET_XB access, the AP pack is primed for an UPDATE_XB. ________________________________________________
-
- If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is
- based on a given starting point. This simplifies index access in
- multi-threaded code, where another thread may have altered the last key
- accessed in the index file. This mode lets you set a starting point for the
- operation by supplying in AP.keyPtr the key value to start at.
-
- For example, say you use GET_FIRST_XB. On return, AP.keyPtr has the the very
- first key. Say elsewhere in your multi-threaded program, another operation
- accesses that same index file handle, and performs some other access, where
- the last accessed key is no longer the same (i.e., not the first key). Your
- first thread is expecting that a GET_NEXT_XB would get the second key,
- however, it very likely won't since the second thread has altered the last
- accessed key for that file handle. By using the atomic mode for key access,
- your first thread, which has the first key value in its AP.keyPtr, can do a
- call to GET_NEXT_XB and get expected results, since the NEXT operation first
- positions to the value in AP.keyPtr and then follows up with a GET_NEXT
- operation. This is performed within the Bullet kernel, and so won't be
- interrupted by another thread (i.e., it is an atomic operation). For this to
- work, you must ensure that the AP.keyPtr value is set to the value of the last
- accessed key. This will always be the case unless uninitialized, or you are
- using global variables for your threads' AP (AccessPack). On return from the
- operation, AP.keyPtr will once again be set up for another atomic operation.
-
- Note: You must supply a valid key value for this atomic access mode.
- AP.keyPtr must be at least as large as the key length in all cases, and is to
- have the starting point for the operation (i.e., the last accessed key). You
- may, alternatively, set the first byte of the key buffer to 0 (but not
- AP.keyPtr itself to NULL). This disables atomic mode for that access, and
- reverts to the internally-stored last key accessed as the starting point.
-
-
- ΓòÉΓòÉΓòÉ 9.54. GET_PREV_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recPtr *AP.recPtr
- AP.keyPtr *AP.keyPtr
-
- Retrieve the previous indexed key and its data record.
-
- The key returned includes an enumerator if the index file uses non-unique keys
- (DUPS_ALLOWED).
-
- This routine is typically called after the index file has first been
- positioned to a known key using either GET_LAST_XB or GET_EQUAL_XB, or after a
- previous GET_PREV_XB or even GET_NEXT_XB. What it basically does is get the
- key and data record preceding the current key, and then makes that key the new
- current key.
-
- This routine, like all the high-level GET_XB routines, fills in the AP.recNo
- of the record accessed. In this case, it fills AP.recNo with the record
- number pointed to by the previous key (now the current key). Since this is
- done upon each GET_XB access, the AP pack is primed for an UPDATE_XB. ________________________________________________
-
- If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is
- based on a given starting point. This simplifies index access in
- multi-threaded code, where another thread may have altered the last key
- accessed in the index file. This mode lets you set a starting point for the
- operation by supplying in AP.keyPtr the key value to start at.
-
- For example, say you use GET_FIRST_XB. On return, AP.keyPtr has the the very
- first key. Say elsewhere in your multi-threaded program, another operation
- accesses that same index file handle, and performs some other access, where
- the last accessed key is no longer the same (i.e., not the first key). Your
- first thread is expecting that a GET_NEXT_XB would get the second key,
- however, it very likely won't since the second thread has altered the last
- accessed key for that file handle. By using the atomic mode for key access,
- your first thread, which has the first key value in its AP.keyPtr, can do a
- call to GET_NEXT_XB and get expected results, since the NEXT operation first
- positions to the value in AP.keyPtr and then follows up with a GET_NEXT
- operation. This is performed within the Bullet kernel, and so won't be
- interrupted by another thread (i.e., it is an atomic operation). For this to
- work, you must ensure that the AP.keyPtr value is set to the value of the last
- accessed key. This will always be the case unless uninitialized, or you are
- using global variables for your threads' AP (AccessPack). On return from the
- operation, AP.keyPtr will once again be set up for another atomic operation.
-
- Note: You must supply a valid key value for this atomic access mode.
- AP.keyPtr must be at least as large as the key length in all cases, and is to
- have the starting point for the operation (i.e., the last accessed key). You
- may, alternatively, set the first byte of the key buffer to 0 (but not
- AP.keyPtr itself to NULL). This disables atomic mode for that access, and
- reverts to the internally-stored last key accessed as the starting point.
-
-
- ΓòÉΓòÉΓòÉ 9.55. GET_LAST_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recPtr *AP.recPtr
- AP.keyPtr *AP.keyPtr
-
- Retrieve the last indexed key and its data record.
-
- The key returned includes an enumerator if the index file uses non-unique keys
- (DUPS_ALLOWED).
-
- This routine is typically used to process a database in reverse index order
- starting at the last ordered key (and its data record). After processing this
- last entry, subsequent reverse-order access of the database is achieved by
- using GET_PREV_XB, until the top of the database is reached, at which point an
- error is returned.
-
- This routine, like all the high-level GET_XB routines, fills in the AP.recNo
- of the record accessed. In this case, it fills AP.recNo with the record
- number pointed to by the last key. Since this is done upon each GET_XB access,
- the AP pack is primed for an UPDATE_XB
-
-
- ΓòÉΓòÉΓòÉ 9.56. INSERT_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.recNo *AP.keyPtr
- AP.recPtr
- AP.keyPtr
- AP.nextPtr
-
- Append the data records to data files and build and insert the related keys
- into all linked index files. (Alternate forms are possible.)
-
- This routine is used to add new entries into a database. Up to 256 index
- files may be inserted into per call, with up to 256 data files being added,
- too, for a total of 512 files managed per single INSERT_XB call.
-
- Note: Bullet comes in 100, 250, and 1024-file versions and so this routine is
- able to use as many files as handles are still available. If non-standard
- fields are used (i.e., non-char structure members to match non-ASCII data
- fields in your non-standard DBF), then be aware that your compiler more than
- likely will add padding to align on member-size boundaries. This will result
- in a mis-match between your compiler structure (rectype above) and your DBF
- structure (as described in fieldList[]). To prevent this, place #pragma
- pack(1) / #pragma pack() around your structures that BULLET uses. Consult
- your particular compiler for alternate methods if it does not support #pragma
- pack.
-
- Only index handles are listed in AP.handle. Each index file has associated
- with it a data file, known internally to BULLET (the xbLink from OPEN_XB).
- There may be more than one index file for a data file, but there is always one
- data file per index handle specified in the list. In other words, you can
- list five index files, each indexing the same xbLink data file, and have
- BULLET perform an atomic insert of that list. Or, another possibility is that
- you have a single index file, indexing a single data file. Or, you can list
- 256 index files, each indexing a single data file (512 total files).
-
- This, and several other routines, are transaction-list-based. This means that
- if a failure occurs prior to the routine's completion, all changes made to the
- database by the routine will be backed-out, and the database (data and index
- files) effectively restored to its original state.
-
- If the routine failed to complete, the function return value is the number
- (1-based) of the pack that caused the failure. A positive number indicates
- the failure was from an index operation; a negative number indicates the
- failure was from a data operation. In each case, the absolute value of the
- return code is the list item that failed (the pack index). For example, if
- five index handles are in the list (AP[0] to AP[4]), and an error occurred on
- the last pack's index file, the return code would be positive 5, indicating
- the fifth pack (AP[4]) failed. Since it was a positive 5, the index file was
- being processed when the error occurred. Being processed means not only
- physical access, but verification, etc. If the return code was -5, then
- again, the error was in the fifth pack, but since it is negative, the error
- occurred while processing the data file. In either case, upon return, the
- database is effectively restored to the way it was before the INSERT_XB call
- was made. Remedy the error, if possible, and INSERT_XB again.
-
- Each pack must include a separate key buffer. You must not share a common key
- buffer. Doing so disables any chance of recovering the index files in case of
- error, since it is in these buffers that BULLET places the newly built keys,
- and it is from these that BULLET, upon an error condition, deletes the keys
- (required for roll-back).
-
- The enumerator is automatically set up by this routine, if required
- (DUPS_ALLOWED and the key already exists with enumerator 0). It does this by
- seeking the last possible enumerator value (0xFFFF) and then backing up to the
- previous key. That key's enumerator is evaluated and incremented, and used as
- this key's.
-
- Specifying Files
-
- As mentioned, only the index file handles are specified in AP.handle. Data
- files are implicitly specified by their links to the index files, as specified
- when the index file was opened (OP.xbLink). INSERT_XB can process up to 256
- index files per call. Since each index file requires a data file, this means
- that up to 256 data files can be processed per call, as well. Also possible
- is that all 256 index handles refer to the same, single data file. Yet
- another possibility is that there is 1 index file, and so 1 data file. The
- possibilities can include those and anything in between.
-
- Example: Specifying a single index file
-
- The simplest form is where a single index handle is specified. This implies a
- single data file, too. AccessPack setup for this is:
-
- AP.func = INSERT_XB;
- AP.handle = indexHandle;
- AP.recNo = 0;
- AP.recPtr = &recordStruct; // contents referred to below as *recordStruct
- AP.keyPtr = keyBuffer;
- AP.nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct is used as a new record that is appended to the
- data file. The data file was linked to this index during the index open,
- in OP.xbLink.
-
- 2. A key is built by BULLET, based on the data in *recordStruct, and that
- key is inserted into the index file (AP.handle). Stored with the key is
- the record number of the record added above.
-
- Note: AP.recNo must be set to 0 prior to the call. Any positive number
- results in an error (0x80000000, and negative numbers, may be used when more
- than one AP pack is used - see below).
-
- Upon return, if no error, the return code is 0. AP.recNo is set to the
- physical record number in the data file that *recordStruct was placed. The
- key that was stored, including any enumerator, is in *keyBuffer.
-
- Upon return, and there was an error, the return code is either -1 or 1. If -1,
- the error was caused during processing of the data file portion, and the error
- code itself is in AP.stat. If +1, the error was caused during processing of
- the index file, and the error code itself is in AP.stat, as well. The return
- code is, as in all BULLET transaction-list routines, an index of the AP pack
- that generated the error -- negative if a data file error, positive if an
- index file error. Since this example has only the single pack, only a -1 or
- +1 could be returned, or 0.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- Example: Specifying two index files for a single data file
-
- Two index files, related to the same data file, would set AccessPack to:
-
- AP[0].func = INSERT_XB;
- AP[0].handle = indexHandle_0;
- AP[0].recNo = 0;
- AP[0].recPtr = &recordStruct;
- AP[0].keyPtr = keyBuffer_0;
- AP[0].nextPtr = AP[1];
-
- AP[1].handle = indexHandle_1;
- AP[1].recNo = 0x80000000;
- AP[1].recPtr = &recordStruct;
- AP[1].keyPtr = keyBuffer_1;
- AP[1].nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct is used as a new record that is appended
- (added) to the data file.
-
- 2. A key is built by BULLET, based on the data in *recordStruct, and that
- key is inserted into the index file (AP[0].handle). Stored with the key
- is the record number of the record added above.
-
- 3. A second key is built by BULLET, based on the data in *recordStruct, and
- that key is inserted into the second index file (AP[1].handle). Stored
- with the key is the record number of the record added above.
-
- Note: The 0x80000000 in AP[1].recNo signifies that AP[1] is using the same
- data record that was appended during processing of AP[0]. This results in
- just the one data record being added. AP[1].recPtr must still, however, point
- to the same data as AP[0].recPtr does.
-
- Upon return, if no error, the return code is 0. AP[0].recNo is set to the
- physical record number in the data file that *recordStruct was placed. The
- key that was stored for the first index, including any enumerator, is in the
- buffer at AP[0].keyPtr. AP[1].recNo is set to the same physical record number
- as AP[0].recNo, except that the record number is negative: For example, if
- AP[0].recNo is 22 on return, AP[1].recNo is -22 (the original 0x80000000 value
- is overwritten). The key that was stored for the second index, including any
- enumerator, is in the buffer at AP[1].keyPtr.
-
- Upon return, and there was an error, the return code can be -2, -1, 1, or 2.
- If negative, the error was caused during processing of that AP pack's data
- file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez
- is the return code, and -1 since C arrays start at 0). If the return code was
- positive, the error was caused during processing of that AP pack's index file,
- and the error code itself is in AP[rez-1].stat, as well. The return code is,
- as in all BULLET transaction-list routines, an index of the AP pack that
- generated the error -- negative if a data file error, positive if an index
- file error.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- Example: Specifying two index files for each of two different data files
-
- Four total files: two index files related to one data file, and two other
- index files related to another data file, would set AccessPack to:
-
- AP[0].func = INSERT_XB;
- AP[0].handle = indexHandle_0;
- AP[0].recNo = 0;
- AP[0].recPtr = &recordStruct_0;
- AP[0].keyPtr = keyBuffer_0;
- AP[0].nextPtr = AP[1];
-
- AP[1].handle = indexHandle_1;
- AP[1].recNo = 0x80000000;
- AP[1].recPtr = &recordStruct_0;
- AP[1].keyPtr = keyBuffer_1;
- AP[1].nextPtr = AP[2];
-
- AP[2].handle = indexHandle_2;
- AP[2].recNo = 0;
- AP[2].recPtr = &recordStruct_1;
- AP[2].keyPtr = keyBuffer_2;
- AP[2].nextPtr = AP[3];
-
- AP[3].handle = indexHandle_3;
- AP[3].recNo = 0x80000000;
- AP[3].recPtr = &recordStruct_1;
- AP[3].keyPtr = keyBuffer_3;
- AP[3].nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct_0 is used as a new record that is appended to
- the data file linked to the index file in AP[0].handle.
-
- 2. A key is built by BULLET, based on the data in *recordStruct_0, and that
- key is inserted into the index file (AP[0].handle). Stored with the key
- is the record number of the record added above, for _0.
-
- 3. A second key is built by BULLET, based on the data in *recordStruct_0,
- and that key is inserted into the second index file (AP[1].handle).
- Stored with the key is the record number of the record added above, using
- *recordStruct_0.
-
- 4. The data in *recordStruct_1 is used as a new record that is appended to
- the data file linked to the index file in AP[2].handle.
-
- 5. A third key is built by BULLET, based on the data in *recordStruct_1, and
- that key is inserted into the index file (AP[2].handle). Stored with the
- key is the record number of the record added above, for _1.
-
- 6. A fourth key is built by BULLET, based on the data in *recordStruct_1,
- and that key is inserted into the fourth index file (AP[3].handle).
- Stored with the key is the record number of the record added above, using
- *recordStruct_1.
-
- Note: The 0x80000000 in AP[1].recNo signifies that AP[1] is using the same
- data record that was appended during processing of AP[0]. This results in
- just the one data record being added. AP[1].recPtr must still, however, point
- to the same data as AP[0].recPtr does. The same applies to AP[2] and AP[3]
- (though different values, of course).
-
- Upon return, if no error, the return code is 0. AP[0].recNo is set to the
- physical record number in the data file that *recordStruct_0 was placed. The
- key that was stored for the first index, including any enumerator, is in the
- buffer at AP[0].keyPtr. AP[1].recNo is set to the same physical record number
- as AP[0].recNo, except that the record number is negative: For example, if
- AP[0].recNo is 22 on return, AP[1].recNo is -22 (the original 0x80000000 value
- is overwritten). The key that was stored for the second index, including any
- enumerator, is in the buffer at AP[1].keyPtr. AP[2].recNo is set to the
- physical record number in the data file that *recordStruct_1 was placed. The
- key that was stored for the third index, including any enumerator, is in the
- buffer at AP[2].keyPtr. AP[3].recNo is set to the same physical record number
- as AP[2].recNo, except that the record number is negative: For example, if
- AP[2].recNo is 74 on return, AP[3].recNo is -74 (the original 0x80000000 value
- is overwritten). The key that was stored for the fourth index, including any
- enumerator, is in the buffer at AP[3].keyPtr.
-
- Upon return, and there was an error, the return code can be -4 to -1, or 1 to
- 4. If negative, the error was caused during processing of that AP pack's data
- file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez
- is the return code, and -1 since C arrays start at 0). If the return code was
- positive, the error was caused during processing of that AP pack's index file,
- and the error code itself is in AP[rez-1].stat, as well. The return code is,
- as in all BULLET transaction-list routines, an index of the AP pack that
- generated the error -- negative if a data file error, positive if an index
- file error.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- Example: Specifying two index files for two records in the same data file
-
- Three files: two index files related to one data file, where two data records
- are to be appended, would set AccessPack to:
-
- AP[0].func = INSERT_XB;
- AP[0].handle = indexHandle_0;
- AP[0].recNo = 0;
- AP[0].recPtr = &recordStruct_0;
- AP[0].keyPtr = keyBuffer_0;
- AP[0].nextPtr = AP[1];
-
- AP[1].handle = indexHandle_1;
- AP[1].recNo = 0x80000000;
- AP[1].recPtr = &recordStruct_0;
- AP[1].keyPtr = keyBuffer_1;
- AP[1].nextPtr = AP[2];
-
- AP[2].handle = indexHandle_0;
- AP[2].recNo = 0;
- AP[2].recPtr = &recordStruct_1;
- AP[2].keyPtr = keyBuffer_2;
- AP[2].nextPtr = AP[3];
-
- AP[3].handle = indexHandle_1;
- AP[3].recNo = 0x80000000;
- AP[3].recPtr = &recordStruct_1;
- AP[3].keyPtr = keyBuffer_3;
- AP[3].nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct_0 is used as a new record that is appended to
- the data file linked to the index file in AP[0].handle.
-
- 2. A key is built by BULLET, based on the data in *recordStruct_0, and that
- key is inserted into the index file (AP[0].handle). Stored with the key
- is the record number of the record added above, for _0.
-
- 3. A second key is built by BULLET, based on the data in *recordStruct_0,
- and that key is inserted into the second index file (AP[1].handle).
- Stored with the key is the record number of the record added above, using
- *recordStruct_0.
-
- 4. The data in *recordStruct_1 is used as a new record that is appended to
- the data file linked to the index file in AP[2].handle. Since
- AP[2].handle is the same index file as that of AP[0].handle, this means
- it's also the same data file as was just operated on above -- a second
- data record is appended to the data file. The net effect of this
- operation is to call INSERT_XB twice, once for one insert, then again for
- the second. The difference is that the operation is atomic -- if one
- fails, the other is not committed; it's an "all or nothing" operation.
-
- 5. A third key is built by BULLET, based on the data in *recordStruct_1, and
- that key is inserted into the index file (AP[2].handle). Stored with the
- key is the record number of the record added directly above, for _1. Note
- that this index file is the same as specified in AP[0].handle.
-
- 6. A fourth key is built by BULLET, based on the data in *recordStruct_1,
- and that key is inserted into the fourth index file (AP[3].handle).
- Stored with the key is the record number of the record added above, using
- *recordStruct_1.
- The return ritual is as described above, for "Specifying two index files each
- for two different data files".
-
- Example: Specifying a single index file for a previously added data record
-
- This form lets you insert a key without adding a data record. This would be
- required if you were, for example, creating a temporary index of select
- records in a data file (i.e., the data records already exist, you just want to
- index them). AccessPack setup for this is:
-
- AP.func = INSERT_XB;
- AP.handle = indexHandle;
- AP.recNo = -recordNumberOfExistingRecord;
- AP.recPtr = &recordStruct;
- AP.keyPtr = keyBuffer;
- AP.nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. A key is built by BULLET, based on the data in *recordStruct, and that
- key is inserted into the index file (AP.handle). Stored with the key is
- the absolute value of the record number specified in AP.recNo (which is
- set to negative record number).
-
- Upon return, if no error, the return code is 0. AP.recNo is changed to
- abs(AP.recNo). The key that was stored, including any enumerator, is in
- *keyBuffer. No data file access is made.
-
- Upon return, and there was an error, the return code is either -1 or 1. If -1,
- the error was caused during processing of the data file portion, and the error
- code itself is in AP.stat. If +1, the error was caused during processing of
- the index file, and the error code itself is in AP.stat, as well. The return
- code is, as in all BULLET transaction-list routines, an index of the AP pack
- that generated the error -- negative if a data file error, positive if an
- index file error. Since this example has only the single pack, only a -1 or
- +1 could be returned.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- An example use of this INSERT_XB feature is to create an ad hoc index of, say,
- records marked as deleted. To do this, create a new index file (say, with a
- key of NAME). Get each data record, by record number using GET_RECORD_XB (for
- records 1 to number-of-records), and check the .tag byte. If '*', call
- INSERT_XB with the negative value of AP.recNo. Do this for every such marked
- record. After all records are processed, you have an index of all deleted
- records in the data file. Delete the index when no longer needed. That's
- just one example.
-
-
- ΓòÉΓòÉΓòÉ 9.57. UPDATE_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle *AP.keyPtr
- AP.recNo
- AP.recPtr
- AP.keyPtr
- AP.nextPtr
-
- Update any and all files in the transaction list if necessary, including both
- index and data files.
-
- This routine is used to update data records while also updating the index
- files if a key field has changed due to data record updates. Up to 256 index
- files may be updated per call, as well as 256 data files, too, for a total of
- 512 files managed per single UPDATE_XB call.
-
- Only index handles are listed in AP.handle. Each index file has associated
- with it a data file, known internally to BULLET (the xbLink from OPEN_XB).
- There may be more than one index file for a data file, but there is always one
- data file per index handle specified in the list. In other words, you can
- list five index files, each indexing the same xbLink data file, and have
- BULLET perform an atomic update of that list. Or, another possibility is that
- you have a single index file, indexing a single data file. Or, you can list
- 256 index files, each indexing a single data file (512 total files).
-
- This, and several other routines, are transaction-list-based. This means that
- if a failure occurs prior to the routine's completion, all changes made to the
- database by the routine will be backed-out, and the database (data and index
- files) effectively restored to its original state.
-
- If the routine failed to complete, the function return value is the number
- (1-based) of the pack that caused the failure. A positive number indicates
- the failure was from an index operation; a negative number indicates the
- failure was from a data operation. In each case, the absolute value of the
- return code is the list item that failed (the pack index). For example, if
- five index handles are in the list(AP[0] to AP[4]), and an error occurred on
- the last pack's index file, the return code would be positive 5, indicating
- the fifth pack (AP[4]) failed. Since it was a positive 5, the index file was
- being processed when the error occurred. Being processed means not only
- physical access, but verification, etc. If the return code was -5, then
- again, the error was in the fifth pack, but since it is negative, the error
- occurred while processing the data file. In either case, upon return, the
- database is restored to the way it was before the UPDATE_XB call was made.
- Remedy the error, if possible, and UPDATE_XB again.
-
- Each pack must include a separate key buffer. You must not share a common key
- buffer. Doing so disables any chance of recovering the index files in case of
- error, since it is in these buffers that BULLET places any newly built keys,
- and it is from these that BULLET, upon an error condition, deletes these keys
- (required for roll-back).
-
- The enumerator is automatically maintained by this routine, if required
- (DUPS_ALLOWED and the key already exists with enumerator 0). The process is
- the same as INSERT_XB's.
-
- How an update works
-
- All data records specified in the list are read from disk into memory, except
- those with AP.recNo=0. Therefore, a memory allocation large enough to store
- all unique data records is made upon entry to this routine (and released at
- exit). For example, if the list includes two implicit data files, and the
- record lengths of those two data files are 2048 and 4096 bytes, an allocation
- of 6K is made. In addition, 40KB more is allocated for workspace. So, for
- this example, 46K is allocated (rounded up to 48KB, the next 4KB page
- boundary). Since up to 256 unique records are possible, where a unique record
- is identified by handle/record number, be aware of the memory requirements if
- you are updating very large databases (e.g., 256 unique records, each 4KB in
- length, would have UPDATE_XB allocate a bit over 1MB of memory for this call).
-
- After the data records have been read from disk, each list-item is processed,
- in order. The disk record image previously read is compared with the record
- image at AP.recPtr. If the same, that item is skipped, and the next item in
- the list is processed. If you know beforehand that that record is the same,
- set that item's AP.recNo=0 so you can avoid having its disk image read and
- stored (or do not include it in the list at all). If the images differ,
- BULLET creates a key for the index file being processed, for each record image
- (the original and the one in AP.recPtr). If the keys generated are the same,
- no index file update is needed. If different, the original key for that
- record is deleted from that index file, and the new key inserted. Finally, the
- new record replaces the old, the new directly overwriting the original. Note
- that the actual sequence of the update event differs somewhat from this
- description in order to optimize the process.
-
- Specifying Files
-
- As mentioned, only the index file handles are specified in AP.handle. Data
- files are implicitly specified by their links to the index files, as specified
- when the index file was opened (OP.xbLink). UPDATE_XB can process up to 256
- index files per call. Since each index file requires a data file, this means
- that up to 256 data files can be processed per call as well. Also possible is
- that all 256 index handles refer to the same, single data file. Yet another
- possibility is that there is 1 index file, and so 1 data file. The
- possibilities can include those and anything in between.
-
- Example: Specifying a single index file
-
- The simplest form is where a single index handle is specified. This implies a
- single data file, too. AccessPack setup for this is:
-
- AP.func = UPDATE_XB;
- AP.handle = indexHandle;
- AP.recNo = recordToUpdate;
- AP.recPtr = &recordStruct;
- AP.keyPtr = keyBuffer;
- AP.nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct is used as the new record that is to replace
- the data record at AP.recNo. The data file was linked to this index file
- (AP.handle) during the index open, in OP.xbLink.
-
- 2. If the record data in *recordStruct is the same as the original disk
- record, nothing is done. If the data is new, the key fields are compared
- to that belonging to the original disk record, and if the same, only the
- record data is updated. If the new record's key differs from the
- original's, the original key for this record is removed from the index,
- and the new key inserted.
-
- AP.recNo must be set to the record number that you are updating. Any GET_XB
- routine (GET_EQUAL_XB, etc.) may be used to identify the number of a data
- record. Key access has the obvious advantage of knowing the record number of
- a specific key (for example, Betty Barbar's data). Any record number, from 1
- to number of records in the data file, can be used. In addition, a negative
- record number can be used. This is treated exactly the same as a positive
- record number (the absolute value is used). The reason this is allowed is
- because INSERT_XB replaces 0x80000000 record numbers with the negative value
- of the previous insert.
-
- Upon return, if no error, the return code is 0. If the record data was new,
- the key for that data record, including any enumerator, is in *keyBuffer.
- This is so even if key fields had not changed.
-
- Upon return, and there was an error, the return code is either -1 or 1. If -1,
- the error was caused during processing of the data file portion, and the error
- code itself is in AP.stat. If +1, the error was caused during processing of
- the index file, and the error code itself is in AP.stat, as well. The return
- code is, as in all BULLET transaction-list routines, an index of the AP pack
- that generated the error -- negative if a data file error, positive if an
- index file error. Since this example has only the single pack, only a -1 or
- +1 could be returned.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- Example: Specifying two index files for a single data file
-
- Two index files, related to the same data file, would set AccessPack to:
-
- AP[0].func = UPDATE_XB;
- AP[0].handle = indexHandle_0;
- AP[0].recNo = recordToUpdate;
- AP[0].recPtr = &recordStruct;
- AP[0].keyPtr = keyBuffer_0;
- AP[0].nextPtr = AP[1];
-
- AP[1].handle = indexHandle_1;
- AP[1].recNo = recordToUpdate;
- AP[1].recPtr = &recordStruct;
- AP[1].keyPtr = keyBuffer_1;
- AP[1].nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct is used as the new record that is to replace
- the data record at AP.recNo. The data file was linked to this index file
- (AP.handle) during the index open, in OP.xbLink.
-
- 2. If the record data in *recordStruct is the same as the original disk
- record, nothing is done. If the data is new, the key fields are compared
- to that belonging to the original disk record, and if the same, only the
- record data is updated. If the new record's key differs from the
- original's, the original key for this record is removed from the index,
- and the new key inserted.
-
- 3. The operation performed directly above is repeated, this time for the
- second index file. The new record data, and the record number to update
- are, for this particular example, the same.
-
- AP.recNo must be set to the record number that you are updating. Each
- AP[].recNo must be set to a valid record number, even if the record number is
- the same as the previous AP[] pack's (the case where you have more than one
- index file for a data file). BULLET knows if the record number duplicates a
- number in a previous AP pack, and allocates resources for only the first
- encounter of the data record. Subsequent encounters refer to the first.
-
- Upon return, if no error, the return code is 0. If the new and original data
- records differ, the key for the new data record, including any enumerator, is
- in the buffer at AP[0].keyPtr. This even if the key fields did not change.
- The same applies to the second index, with the new data key in AP[1].keyPtr.
-
- Upon return, and there was an error, the return code can be -2, -1, 1, or 2.
- If negative, the error was caused during processing of that AP pack's data
- file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez
- is the return code, and -1 since C arrays start at 0). If the return code was
- positive, the error was caused during processing of that AP pack's index file,
- and the error code itself is in AP[rez-1].stat, as well. The return code is,
- as in all BULLET transaction-list routines, an index of the AP pack that
- generated the error -- negative if a data file error, positive if an index
- file error.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- Example: Specifying two index files for each of two different data files
-
- Four total files: two index files related to one data file, and two other
- index files related to another data file, would set AccessPack to:
-
- AP[0].func = UPDATE_XB;
- AP[0].handle = indexHandle_0;
- AP[0].recNo = recordToUpdate_0;
- AP[0].recPtr = &recordStruct_0;
- AP[0].keyPtr = keyBuffer_0;
- AP[0].nextPtr = AP[1];
-
- AP[1].handle = indexHandle_1;
- AP[1].recNo = recordToUpdate_0;
- AP[1].recPtr = &recordStruct_0;
- AP[1].keyPtr = keyBuffer_1;
- AP[1].nextPtr = AP[2];
-
- AP[2].handle = indexHandle_2;
- AP[2].recNo = recordToUpdate_1;
- AP[2].recPtr = &recordStruct_1;
- AP[2].keyPtr = keyBuffer_2;
- AP[2].nextPtr = AP[3];
-
- AP[3].handle = indexHandle_3;
- AP[3].recNo = recordToUpdate_1;
- AP[3].recPtr = &recordStruct_1;
- AP[3].keyPtr = keyBuffer_3;
- AP[3].nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct_0 is used as the new record that is to replace
- the data record at AP[0].recNo in the data file linked to the index file
- in AP[0].handle.
-
- 2. If the record data in *recordStruct_0 is the same as the original disk
- record, nothing is done. If the data is new, the key fields are compared
- to that belonging to the original disk record, and if the same, only the
- record data is updated. If the new record's key differs from the
- original's, the original key for this record is removed from the index,
- and the new key inserted.
-
- 3. The operation performed directly above is repeated, this time for the
- second index file. The new record data, and the record number to update,
- are for this particular example, the same.
-
- 4. The data in *recordStruct_1 is used as the new record that is to replace
- the data record at AP[2].recNo in the data file linked to the index file
- in AP[2].handle.
-
- 5. If the record data in *recordStruct_1 is the same as the original disk
- record, nothing is done. If the data is new, the key fields are compared
- to that belonging to the original disk record, and if the same, only the
- record data is updated. If the new record's key differs from the
- original's, the original key for this record is removed from the index,
- and the new key inserted.
-
- 6. The operation performed directly above is repeated, this time for the
- fourth index file. The new record data, and the record number to update
- are, for this particular example, the same.
-
- Upon return, if no error, the return code is 0. If the new and original data
- records differ, the keys for the new data records, including any enumerators,
- are in the buffers at AP[0].keyPtr to AP[3].keyPtr. This even if the key
- fields did not change. If one, or all, of the new data records matched the
- original data record, nothing is placed in *keyBuffer for that index.
-
- Upon return, and there was an error, the return code can be -4 to -1, or 1 to
- 4. If negative, the error was caused during processing of that AP pack's data
- file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez
- is the return code, and rez-1 since C arrays start at 0). If the return code
- was positive, the error was caused during processing of that AP pack's index
- file, and the error code itself is in AP[rez-1].stat, as well. The return
- code is, as in all BULLET transaction-list routines, an index of the AP pack
- that generated the error -- negative if a data file error, positive if an
- index file error.
-
- Note: If an error occurred after any part of the database had changed (during
- this particular call), then any and all changes that were made are backed-out,
- and the files restored to the same state as before the call.
-
- Example: Specifying two index files for two records in the same data file
-
- Three files: two index files related to one data file, where two data records
- are to be updated, would set AccessPack to:
-
- AP[0].func = UPDATE_XB;
- AP[0].handle = indexHandle_0;
- AP[0].recNo = recordToUpdate_0;
- AP[0].recPtr = &recordStruct_0;
- AP[0].keyPtr = keyBuffer_0;
- AP[0].nextPtr = AP[1];
-
- AP[1].handle = indexHandle_1;
- AP[1].recNo = recordToUpdate_0;
- AP[1].recPtr = &recordStruct_0;
- AP[1].keyPtr = keyBuffer_1;
- AP[1].nextPtr = AP[2];
-
- AP[2].handle = indexHandle_0;
- AP[2].recNo = recordToUpdate_1;
- AP[2].recPtr = &recordStruct_1;
- AP[2].keyPtr = keyBuffer_2;
- AP[2].nextPtr = AP[3];
-
- AP[3].handle = indexHandle_1;
- AP[3].recNo = recordToUpdate_1;
- AP[3].recPtr = &recordStruct_1;
- AP[3].keyPtr = keyBuffer_3;
- AP[3].nextPtr = NULL;
-
- A call to BULLET with the above does the following:
-
- 1. The data in *recordStruct_0 is used as the new record that is to replace
- the data record at AP[0].recNo in the data file linked to the index file
- in AP[0].handle.
-
- 2. If the record data in *recordStruct_0 is the same as the original disk
- record, nothing is done. If the data is new, the key fields are compared
- to that belonging to the original disk record, and if the same, only the
- record data is updated. If the new record's key differs from the
- original's, the original key for this record is removed from the index,
- and the new key inserted.
-
- 3. The operation performed directly above is repeated, this time for the
- second index file. The new record data, and the record number to update,
- are for this particular example, the same.
-
- 4. The data in *recordStruct_1 is used as the new record that is to replace
- the data record at AP[2].recNo in the data file linked to the index file
- in AP[2].handle. This is the same index file as the first AP pack, and
- also the same data file. However, this is a different record number.
-
- 5. If the record data in *recordStruct_1 is the same as the original disk
- record, nothing is done. If the data is new, the key fields are compared
- to that belonging to the original disk record, and if the same, only the
- record data is updated. If the new record's key differs from the
- original's, the original key for this record is removed from the index,
- and the new key inserted.
-
- 6. The operation performed directly above is repeated, this time for the
- fourth index file. The new record data, and the record number to update
- are, for this particular example, the same.
- The return ritual is as described above, for "Specifying two index files each
- for two different data files".
-
-
- ΓòÉΓòÉΓòÉ 9.58. REINDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: ACCESSPACK Source Example
-
- IN OUT
- AP.func AP.stat
- AP.handle AP.recNo
- AP.keyPtr *AP.keyPtr
- AP.nextPtr
-
- Reindex all files in the transaction list, re-evaluating the key expression in
- the process.
-
- This routine is used to reindex up to 256 index files per call. The index
- files must already exist and be open. Any existing key values are overwritten
- by new key data. In other words, if you have a 100MB index file, REINDEX_XB
- uses the same file space, building new keys over old. This results in a less
- fragmented disk and also minimizes disk space needed. You can also create a
- new, empty index file and reindex to that. This would be useful, for
- instance, if you needed to create a temporary index file -- something that
- you'd use for a report, say, then delete after the report. Another use for
- creating a new index file and reindexing to that is to, after creating it
- (COPY_INDEX_HEADER_XB can be used), use EXPAND_FILE_DOS and expand it to the
- expected size. This has the benefit of ensuring that this file allocation is
- as contiguous as the file system allows (without relying on OS/API-specific
- calls).
-
- If the routine failed to complete, the function return value is the number
- (1-based) of the pack that caused the failure. A positive number indicates
- the failure was from an index operation; a negative number indicates the
- failure was from a data operation (reading the data file). In each case, the
- absolute value of the return code is the list item that failed (the pack
- index). For example, if five index handles are in the list(AP[0] to AP[4]),
- and an error occurred on the last pack's index file, the return code would be
- positive 5, indicating the fifth pack (AP[4]) failed. Since it was a positive
- 5, the index file was being processed when the error occurred. Being
- processed means not only physical access, but verification, etc. If the
- return code was -5, then again, the error was in the fifth pack, but since it
- is negative, the error occurred while processing the data file.
-
- Unlike INSERT_XB and UPDATE_XB, each pack need not include a separate key
- buffer; you may share a common key buffer. If duplicate keys are generated in
- the reindex process and the sort function does not flag DUPS_ALLOWED, an error
- is returned. The duplicate key is in *AP.keyPtr and the record number it was
- generated from in AP.recNo. Since no roll-back is performed, there is only a
- real need for a single key buffer. You may use separate ones, too.
-
- This routine creates a temporary work file in either the current directory or,
- if the environment variable TMP is defined, in the directory pointed to by
- TMP=. The path used for this temporary file may also be specified at run-time
- by using the TMP_PATH_PTR item for SET_SYSVARS_XB. If TMP_PATH_PTR is NULL
- (default), then TMP= is used, or if that is not found, then the current
- directory is used. The size of this temporary file is, in bytes,
- approximately (keylength+4) * number of records in the data file. The
- resultant index files are, by default, optimized for minimum size and maximum
- retrieval speed. This full-node packing leaves one empty key per node, which
- means b-tree splitting will occur almost immediately upon inserting data (with
- INSERT_XB or STORE_KEY_XB).
-
- This behaviour can be modified with the REINDEX_PACK_PCT item for
- SET_SYSVARS_XB so that less packing is done. Less packing would improve
- subsequent INSERT_XB performance since all nodes are not almost full as they
- are with a full pack. File size and retrieval times increase, though, but
- perhaps not noticeably.
-
- During the reindex process, each record is checked for a matching skip-tag
- value, as set in SET_SYSVARS_XB. The skip-tag is set to 0 by default, where
- no check is done and keys for all records in the data file are inserted into
- the index file.
-
-
- ΓòÉΓòÉΓòÉ 9.59. LOCK_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.xlMode
- LP.dlMode
- LP.recStart=0
- LP.nextPtr
-
- Lock all bytes of the files in the list for exclusive use by the current
- process, and reload file headers from disk. LP.recStart must be 0 for each
- pack.
-
- This routine is used to lock the database for either exclusive use by this
- process, or shared access (allowing any process to read, but not write, to the
- files). Up to 256 index files may be locked per call, as well as 256 data
- files, too, for a total of 512 files per single LOCK_XB call. Shared-access
- locking prevents all processes from writing to the file while a shared lock is
- in force, including this process. To relock in exclusive lock mode, without
- unlocking first, use: RELOCK_XB.
-
- Only index handles are listed in AP.handle. Each index file has associated
- with it a data file, known internally to BULLET (the xbLink from OPEN_XB).
- There may be more than one index file for a data file, but there is always one
- data file per index handle specified in the list. For example, you can list
- five index files, each indexing the same xbLink data file, and have BULLET
- perform an atomic lock of that list.
-
- LP.xlMode is set to 1 to perform a shared lock on the index file. Set to 0
- for an exclusive lock. A shared lock allows only reading.
-
- LP.dlMode is set to 1 to perform a shared lock on the data file. Set to 0 for
- an exclusive lock. A shared lock allows only reading.
-
- The lock mode (shared <-> exclusive) can be changed using RELOCK_XB.
-
- Bullet maintains a per-handle lock count, and does a physical region lock only
- upon the first lock request (or on a relock request). It is only on this
- first lock request that the header is reloaded. When the lock count returns
- to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if
- required. Only full-locks are maintained in this way. The number of
- outstanding locks can be determined from the SIP and SDP structures, from the
- STAT_XB routines. Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB
- routines, as well as the data ones, also act upon this lock count. Therefore,
- you can lock a file 100 times in a row, but only on the first lock are any
- operations actually performed, and only on the last unlock are any performed.
- Other lock/unlock calls (other than the first lock or last unlock) simply
- increment or decrement the lock count for that handle.
-
- This, and several other routines, are transaction-list-based. This means that
- if a failure occurs prior to the routine's completion, all locks made to the
- database by this routine will be unlocked.
-
- If the routine failed to complete, the function return value is the number
- (1-based) of the pack that caused the failure. A positive number indicates
- the failure was from an index operation; a negative number indicates the
- failure was from a data operation. In each case, the absolute value of the
- return code is the list item that failed (the pack index). For example, if
- five index handles are in the list(AP[0] to AP[4]), and an error occurred on
- the last pack's index file, the return code would be positive 5, indicating
- the fifth pack (AP[4]) failed. Since it was a positive 5, the index file was
- being processed when the error occurred. Being processed means not only
- physical access, but verification, etc. If the return code was -5, then
- again, the error was in the fifth pack, but since it is negative, the error
- occurred while processing the data file. In either case, upon return, any
- files locked during this call are unlocked before returning.
-
- The advantage of using region locks (LOCK_XB locks entire file regions) to
- control file access is that the file does not need to be opened/closed using
- the Deny Read/Write sharing attribute. Opening the file for Deny None, and
- controlling subsequent access with region locks, allows for faster processing
- since files do not need to be constantly opened and closed, as they would if
- access were controlled by opening with Deny Read/Write.
-
- Using the operating system to control access also prevents other processes
- from accessing the files. Other methods, such as internal locking (such as
- using 'L' in the tag field as a program-aware in-use flag), work fine so long
- as each process accessing the files knows about this internal "locking".
- However, since it's proprietary, other processes may not know about it. Any
- locking system that is not commonly shared throughout the system is not
- effective when it comes to preventing other processes from corrupting files.
-
- Note: Region locking prevents other processes from both writing and reading
- the locked file. For operating systems that do not provide shared locks, and
- read-access is required at all times, you may specify this type access with
- the access-sharing mode when the BULLET file is opened. Once opened for this
- (R/W, DenyWrite) then only the current process can write to the file until it
- is closed. Other processes must open the file for Read-Only access. For
- small networks (two or three nodes), this may be suitable. Otherwise, region
- locking is much preferred, and very much faster, since files do not have to be
- opened and closed every time the access state needs to change.
-
-
- ΓòÉΓòÉΓòÉ 9.60. UNLOCK_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.recStart=0
- LP.nextPtr
-
- Unlock all bytes in the specified files (previously locked) and flush the
- files' headers to disk (the flush is done before the locks are released). Also
- unlock all bytes in the related data file and flush the data file header to
- disk. LP.recStart must be 0 for each pack.
-
- Note: If a shared-lock is active for this handle (as set by this process),
- the flush is not performed. This because writing to the locked region is not
- permitted (nor is the flush required since nothing could have been changed).
-
- If the routine failed to complete, the function return value is the number
- (1-based) of the pack that caused the failure. A positive number indicates
- the failure was from an index operation; a negative number indicates the
- failure was from a data operation. In each case, the absolute value of the
- return code is the list item that failed (the pack index). For example, if
- five index handles are in the list(AP[0] to AP[4]), and an error occurred on
- the last pack's index file, the return code would be positive 5, indicating
- the fifth pack (AP[4]) failed. Since it was a positive 5, the index file was
- being processed when the error occurred. Being processed means not only
- physical access, but verification, etc. If the return code was -5, then
- again, the error was in the fifth pack, but since it is negative, the error
- occurred while processing the data file.
-
- This routine does not attempt to re-lock those files unlocked successfully if
- an error occurs in the transaction. If an error does occur (unlikely), the
- remaining files must be manually unlocked with the UNLOCK_KEY_XB and
- UNLOCK_DATA_XB routines.
-
- Bullet maintains a per-handle lock count, and does a physical region lock only
- upon the first lock request (or on a relock request). It is only on this
- first lock request that the header is reloaded. When the lock count returns
- to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if
- required. Only full-locks are maintained in this way. The number of
- outstanding locks can be determined from the SIP and SDP structures, from the
- STAT_XB routines. Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB
- routines, as well as the data ones, also act upon this lock count. Therefore,
- you can lock a file 100 times in a row, but only on the first lock are any
- operations actually performed, and only on the last unlock are any performed.
- Other lock/unlock calls (other than the first lock or last unlock) simply
- increment or decrement the lock count for that handle.
-
-
- ΓòÉΓòÉΓòÉ 9.61. LOCK_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.xlMode
-
- Lock all bytes of the index file for exclusive use by the current process and
- reload the index file's header from disk.
-
- LP.xlMode is set to 1 to perform a shared lock. Set to 0 for an exclusive
- lock. A shared lock allows only reading. The lock mode (shared <->
- exclusive) can be changed using RELOCK_INDEX_XB.
-
- Bullet maintains a per-handle lock count, and does a physical region lock only
- upon the first lock request (or on a relock request). It is only on this
- first lock request that the header is reloaded. When the lock count returns
- to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if
- required. Only full-locks are maintained in this way. The number of
- outstanding locks can be determined from the SIP and SDP structures, from the
- STAT_XB routines. Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB
- routines, as well as the data ones, also act upon this lock count. Therefore,
- you can lock a file 100 times in a row, but only on the first lock are any
- operations actually performed, and only on the last unlock are any performed.
- Other lock/unlock calls (other than the first lock or last unlock) simply
- increment or decrement the lock count for that handle.
-
-
- ΓòÉΓòÉΓòÉ 9.62. UNLOCK_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
-
- Unlock all bytes in the specified file (previously locked) and flush the
- file's header to disk (the flush is done before the locks are released).
-
- If the current lock state is shared, the flush is not performed.
-
- Bullet maintains a per-handle lock count, and does a physical region lock only
- upon the first lock request (or on a relock request). It is only on this
- first lock request that the header is reloaded. When the lock count returns
- to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if
- required. Only full-locks are maintained in this way. The number of
- outstanding locks can be determined from the SIP and SDP structures, from the
- STAT_XB routines. Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB
- routines, as well as the data ones, also act upon this lock count. Therefore,
- you can lock a file 100 times in a row, but only on the first lock are any
- operations actually performed, and only on the last unlock are any performed.
- Other lock/unlock calls (other than the first lock or last unlock) simply
- increment or decrement the lock count for that handle.
-
-
- ΓòÉΓòÉΓòÉ 9.63. LOCK_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.dlMode
- LP.recStart
- LP.recCount
-
- Lock all bytes of the data file specified in LP.handle for exclusive use by
- the current process. It also reloads the data file header from disk. You must
- set LP.recStart=0 to lock all bytes. To lock a single record, or a set of
- contiguous records, set LP.recStart to the first record to lock and
- LP.recCount to the number of records to lock.
-
- Header re-loading is performed only if locking all bytes.
-
- If LP.recStart is not 0, be aware that the header is not locked, nor is it
- re-loaded. Also, maintaining a lock on the single record prevents any other
- process from doing a full lock on that data file, thereby preventing write
- access to the file from any other BULLET process using LOCK_XB, but not
- necessarily preventing other applications from writing to that file. That may
- or may not be good. It does not prevent other BULLET processes from reading
- that file (except for that locked record).
-
- Multiple single records are allowed, but each must then be unlocked
- individually, in the same format (start, count) as the lock.
-
- LP.dlMode is set to 1 to perform a shared lock. Set to 0 for an exclusive
- lock. A shared lock allows only reading. The lock mode (shared <->
- exclusive) can be changed using RELOCK_DATA_XB.
-
- Bullet maintains a per-handle lock count, and does a physical region lock only
- upon the first lock request (or on a relock request). It is only on this
- first lock request that the header is reloaded. When the lock count returns
- to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if
- required. Only full-locks are maintained in this way. The number of
- outstanding locks can be determined from the SIP and SDP structures, from the
- STAT_XB routines. Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB
- routines, as well as the data ones, also act upon this lock count. Therefore,
- you can lock a file 100 times in a row, but only on the first lock are any
- operations actually performed, and only on the last unlock are any performed.
- Other lock/unlock calls (other than the first lock or last unlock) simply
- increment or decrement the lock count for that handle.
-
-
- ΓòÉΓòÉΓòÉ 9.64. UNLOCK_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.recStart
- LP.recCount
-
- Unlock all bytes in the specified file handle (previously locked) and flush
- the data file header to disk (the flush is done before the lock is released).
- You must set LP.recStart=0 to unlock all bytes. To unlock a single record, or
- a set of contiguous records, set LP.recStart to the first record to unlock and
- LP.recCount to the number of records to unlock.
-
- Header flushing is performed only if unlocking a full lock.
-
- An unlock must exactly mimic its corresponding lock. This means if you lock
- several records singly, you must unlock each of those records -- you cannot
- use LP.recStart=0 to unlock singly-locked records.
-
- Bullet maintains a per-handle lock count, and does a physical region lock only
- upon the first lock request (or on a relock request). It is only on this
- first lock request that the header is reloaded. When the lock count returns
- to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if
- required. Only full-locks are maintained in this way. The number of
- outstanding locks can be determined from the SIP and SDP structures, from the
- STAT_XB routines. Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB
- routines, as well as the data ones, also act upon this lock count. Therefore,
- you can lock a file 100 times in a row, but only on the first lock are any
- operations actually performed, and only on the last unlock are any performed.
- Other lock/unlock calls (other than the first lock or last unlock) simply
- increment or decrement the lock count for that handle.
-
-
- ΓòÉΓòÉΓòÉ 9.65. CHECK_REMOTE_XB ΓòÉΓòÉΓòÉ
-
- Pack: REMOTEPACK Source Example
-
- IN OUT
- RP.func RP.stat
- RP.handle RP.isRemote
- -or- RP.flags=0
- RP.drive RP.isShare=1
-
- If RP.handle is non-zero, determine if the specified handle of a file or
- device is remote. If the handle is local (e.g., not a network file or
- device), RP.isRemote returns 0, otherwise it is remote. RP.flags=0 and
- RP.isShare=1 on return for either a handle or drive check under OS/2.
-
- If RP.handle is zero, determine if the drive specified in RP.drive is remote.
- Drive A: is 1, B: is 2, C: is 3, and so on. To check the default drive use 0
- (the default drive is the current drive). If the drive is local (e.g., not a
- network drive), RP.isRemote returns 0, otherwise it is remote.
-
- The significance of the remote-ness is less important in a multitasking
- environment since sharing of resources (files, in particular) must always be
- managed, compared to single-tasking environments where, typically, sharing
- (locking mechanisms) need only be performed when the resource is able to be
- accessed by another process (ie is a 'network' drive). Note that the resource
- need not be located elsewhere to be classified as remote: Drives or devices or
- files on the same machine may be classified as remote if the network software
- is redirecting local access (such as on a server).
-
- Note: This routine is not mutex-protected.
-
-
- ΓòÉΓòÉΓòÉ 9.66. RELOCK_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.xlMode
- LP.dlMode
- LP.recStart=0
- LP.nextPtr
-
- Relock all bytes of the index files for the mode specified in LP.xlMode (index
- files) and LP.dlMode (data files). Also relock all bytes in the related data
- file. LP.recStart must be 0 for each pack.
-
- Set LP.xlMode=1 to select a shared lock for the index file; set to 0 for an
- exclusive lock. Set LP.dlMode=1 to select a shared lock for the data file;
- set to 0 for an exclusive lock.
-
- If the lock mode is from exclusive to shared, the file is flushed before the
- shared state is set. BULLET maintains the current lock state and knows which
- direction the lock is going in. The lock state (shared or exclusive) can be
- determined by the SIP and SDP structures from the STAT_XB routines. This
- routine does not affect the lock count, nor are the headers reloaded (nor
- should they be).
-
- The lock state is on a file handle basis, not on an LP[] pack basis. This
- means the file, as identified by the handle, is in the lock state last set.
-
- Note: The lock switch is made atomic: Rather than unlocking, and then
- locking again in the new state, this performs all operations without the
- possibility that another process can grab the lock away.
-
-
- ΓòÉΓòÉΓòÉ 9.67. RELOCK_INDEX_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.xlMode
-
- Relock all bytes of the index file for the mode specified in LP.xlMode and
- reload the header.
-
- Set LP.xlMode=1 to select a shared lock; set to 0 for an exclusive lock. If
- the lock mode is from exclusive to shared, the file is flushed before the
- shared state is set. BULLET maintains the current lock state and knows which
- direction the lock is going in. The lock state (shared or exclusive) can be
- determined by the SIP structure from the STAT_INDEX_XB routine. This routine
- does not affect the lock count, nor is the header reloaded (nor should it be).
-
- Note: The lock switch is made atomic: Rather than unlocking, and then
- locking again in the new state, this performs all operations without the
- possibility that another process can grab the lock away.
-
-
- ΓòÉΓòÉΓòÉ 9.68. RELOCK_DATA_XB ΓòÉΓòÉΓòÉ
-
- Pack: LOCKPACK Source Example
-
- IN OUT
- LP.func LP.stat
- LP.handle
- LP.dlMode
- LP.recStart
- LP.recCount
-
- Relock all bytes of the data file for the mode specified in LP.dlMode and
- reload the header, unless LP.recStart is non-zero.
-
- If the lock mode is from exclusive to shared, the file is flushed before the
- shared state is set. BULLET maintains the current lock state and knows which
- direction the lock is going in. The lock state (shared or exclusive) can be
- determined by the SDP structure from the STAT_DATA_XB routine. This routine
- does not affect the lock count, nor is the header reloaded (nor should it be).
-
- You must set LP.recStart=0 to relock all bytes. To relock a single record, or
- set of contiguous records, set LP.recStart=record# to relock and LP.recCount
- to the number of records to relock.
-
- Note: The lock switch is made atomic: Rather than unlocking, and then
- locking again in the new state, this performs all operations without the
- possibility that another process can grab the lock away.
-
-
- ΓòÉΓòÉΓòÉ 9.69. DELETE_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.filenamePtr
-
- Delete the specified file.
-
- Note: OS/2 DosForceDelete is used so the file is not recoverable with the
- UNDELETE command.
-
-
- ΓòÉΓòÉΓòÉ 9.70. RENAME_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.filenamePtr
- DFP.newNamePtr
-
- Rename a file. May also be used to move the file to a new directory within
- the partition.
-
- If the specified directory differs from the file's directory, the file's
- directory entry is moved to the new directory.
-
- For example, if the filenamePtr filename is /LP100/PROJ94A.INF and the
- newFilenamePtr filename is /ARCH/PROJ93A.INA, the file is essentially renamed
- and also moved to the /ARCH directory.
-
-
- ΓòÉΓòÉΓòÉ 9.71. CREATE_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.filenamePtr
- DFP.attr
-
- Create a new file.
-
- The specified filename/pathname must not already exist.
-
- The file created is not left open. You must OPEN_FILE_DOS to use it.
-
- The attribute used during the create can be:
-
- Attribute Value Meaning
- Normal 0 normal access permitted to file
- Read-Only 1 read-only access permitted to file
- Hidden 2 file does not appear in directory listing
- System 4 file is a system file
- SubDir 10h FILENAME is a subdirectory
- Archive 20h file is marked for archiving
-
- Note: Use MAKE_DIR_DOS to create a subdirectory.
-
-
- ΓòÉΓòÉΓòÉ 9.72. ACCESS_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.filenamePtr
- DFP.asMode
-
- Determine if the specified file can be accessed with the specified
- access-sharing mode.
-
- Basically, a Does-File-Exist routine. It uses the specified access-sharing
- mode when trying to open the file. For example, if you specify DFP.attr =
- 0x0042 (R/W access + Deny None sharing) and issue ACCESS_FILE_DOS on a
- Read-Only file, an error is returned. A sharing mode must be specified; it
- cannot be left 0.
-
-
- ΓòÉΓòÉΓòÉ 9.73. OPEN_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.filenamePtr DFP.handle
- DFP.asMode
-
- Open the file with the access-sharing mode, returning the handle on success.
-
-
- ΓòÉΓòÉΓòÉ 9.74. SEEK_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.handle DFP.seekTo
- DFP.seekTo
- DFP.method
-
- Position the file pointer of the file to the seekTo position based on the
- method specified.
-
- The position is a 32-bit value and is relative to either the start of the
- file, the current file pointer position, or the end of the file.
-
- Method Meaning
- 0 start move from the start of file (offset is a 32-bit unsigned value)
- 1 start move at the current position (offset a signed value)
- 2 start move at the end of file (offset a signed value)
- For example, to move to the last byte of a sector (512th byte, but offset
- 511), set the offset value to 511 and use Method 0. On return, the absolute
- offset value of the new position is returned. This return value is useful
- with Method 2 since you can specify an offset of 0 and have the file length
- returned. To move to the start of the file, use method 0, offset 0. To move
- to the first byte of the second sector, use offset 512.
-
- Note: Never position the file pointer to before the start of file.
-
-
- ΓòÉΓòÉΓòÉ 9.75. READ_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.handle DFP.bytes
- DFP.bytes
- DFP.bufferPtr
-
- Read from the file or device the specified number of bytes into a buffer.
-
- On block devices (such as disks) input starts at the current file position and
- the file pointer is repositioned to the last byte read +1.
-
- It is possible to read less than the bytes specified without an error being
- generated. Compare the bytes to read with the returned bytes read value. If
- less then end of file was reached during the read. If 0 then file was already
- at EOF.
-
-
- ΓòÉΓòÉΓòÉ 9.76. EXPAND_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.handle
- DFP.bytes
-
- Expands the file by the number of bytes beyond its current size.
-
- This routine is useful in pre-allocating disk space. By reserving disk space
- in advance you can guarantee that enough disk space will be available for a
- future operation (especially if more than 1 process is running). You'll also
- be able ensure that the disk space that a file does use is as contiguous as
- possible.
-
- Database systems are dynamic and their files typically allocate new space on
- an as-needed basis. This dynamic allocation can cause parts of a file to be
- located throughout the disk system, possibly affecting performance
- drastically. By pre-allocating the disk space you can be assured of
- consistent throughput performance since the file is contiguous.
-
-
- ΓòÉΓòÉΓòÉ 9.77. WRITE_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.handle DFP.bytes
- DFP.bytes
- DFP.bufferPtr
-
- Write to the file or device the specified number of bytes from a buffer.
-
- If the number of bytes written is less than the specified bytes, this routine
- returns an error.
-
- On block devices (such as disk) output starts at the current file position,
- and the file pointer is repositioned to the last byte written +1.
-
- Note: If the specified bytes to write is 0, the file is truncated at the
- current file pointer position.
-
-
- ΓòÉΓòÉΓòÉ 9.78. CLOSE_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.handle
-
- Close the file flushing any internal buffers, releasing any locked regions,
- and updating the directory entry to the correct size, date, and time.
-
-
- ΓòÉΓòÉΓòÉ 9.79. MAKE_DIR_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.filenamePtr
-
- Create a new subdirectory.
-
-
- ΓòÉΓòÉΓòÉ 9.80. COMMIT_FILE_DOS ΓòÉΓòÉΓòÉ
-
- Pack: DOSFILEPACK Source Example
-
- IN OUT
- DFP.func DFP.stat
- DFP.handle
-
- Flushes the OS system buffers for the handle, and updates the directory entry
- for size.
-
-
- ΓòÉΓòÉΓòÉ 10. Bullet Source Examples ΓòÉΓòÉΓòÉ
-
- Bullet source example excerpts for the C language follow. Complete program
- source examples are provide on the included disk.
-
- In the examples, consider that
-
- CHAR keyBuffer[64];
- AP.keyPtr = keyBuffer;
-
- provides the compiler with a pointer whenever 'keyBuffer' is referenced, hence
- no need to use &keyBuffer. Contrast this with a structure definition
-
- struct yourRecordLayout yourRecord;
- AP.recPtr = &yourRecord;
-
- where &yourRecord is required.
-
- Be aware of how you use zero-terminated strings, especially for fields that are
- not to be 0T'ed (DATE and NUMERIC fields, for example). Use sscanf() and
- sprintf(), as required.
-
-
- ΓòÉΓòÉΓòÉ 10.1. Bullet Initialization ΓòÉΓòÉΓòÉ
-
- /* All example source uses minimal error checking/handling throughout */
- // comment marks are used throughout
-
- // To simplify the source examples, error handling is typically of the form:
- //
- // if (rez) goto ErrorHandler;
- //
- // Use suitable coding as you would normally handle the error.
-
- #include "bullet2.h"
-
- INITPACK IP; // packs used here
-
- // INIT_XB must be the first Bullet routine used
-
- IP.func = INIT_XB; // start up Bullet
- IP.JFTsize = 30; // allow at least 30 files to be opened
- rez = BULLET(&IP);
- if (rez) return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.2. Bullet Shutdown ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- EXITPACK EP; // packs used here
-
- EP.func = EXIT_XB; // shutdown Bullet
- rez = BULLET(&EP);
- if (rez) return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.3. Memory Available ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- MEMORYPACK EP; // packs used here
-
- MP.func = MEMORY_XB;
- rez = BULLET(&MP);
- if (rez==0) {
- printf("Memory result is not useful right now, but since you asked,\n");
- printf("the largest unused block of memory is right now at %d bytes\n",MP.memory);
- }
-
-
- ΓòÉΓòÉΓòÉ 10.4. File Backup Procedure ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- // code determines file type of handle, and selects appropriate backup method
- // this type of work (copy/backup) would be a prelude to reindexing an index
- // file or performing a pack on a data file since both of those routines
- // overwrite existing data
-
- COPYPACK CP;
- LOCKPACK LP;
- STATHANDLEPACK SHP;
- STATDATAPACK SDP;
- STATINDEXPACK SIP;
-
- CHAR CopyOfIndexHdrName[260];
- CHAR BackupDataName[260];
-
- // Given a handle, find out its type and, if index, generate a copy
- // of the index file's header, or if data, backup the entire data file.
- // Files are locked to ensure that access if permissible.
-
- SHP.func = STAT_HANDLE_XB;
- SHP.handle = passedHandle;
- rez = BULLET(&SHP);
- if (SHP.ID==-1)
- puts("Handle is not a Bullet data or index file. Use DosCopy() API call.");
- else {
- if (SHP.ID==0) {
-
- // Since the copy takes place on open files, ensure a full-lock is in place
- // A shared lock is okay since this file is only read
-
- // locking the entire file also RELOADS the index header
-
- LP.func = LOCK_INDEX_XB;
- LP.handle = passedHandle;
- LP.xlMode = LOCK_SHARED; // in bullet2.h
- rez = BULLET(&LP);
- if (rez) goto ErrorHandler;
-
- SIP.func = STAT_INDEX_XB;
- SIP.handle = passedHandle;
- rez = BULLET(&SIP);
- if (rez) goto ErrorHandler; // must unlock file in handler
-
- // SIP.filenamePtr -> pathname of this handle, from which you can
- // derive a suitable name for the index header written next -- in
- // case it's not obvious, this function is not part of Bullet
-
- DeriveSuitableName(SIP.filenamePtr,CopyOfIndexHdrName);
-
- CP.func = COPY_INDEX_HEADER_XB;
- CP.handle = passedHandle;
- CP.filenamePtr = CopyOfIndexHdrName;
- rez = BULLET(&CP);
- if (rez) goto ErrorHandler; // must unlock file in handler
-
- // unlocking also flushes the index header if not LOCK_SHARED (and if required)
-
- LP.func = UNLOCK_INDEX_XB;
- LP.handle = passedHandle;
- rez = BULLET(&LP);
- if (rez) goto ErrorHandler;
- }
- else {
-
- // locking the entire file also RELOADS the data header
-
- LP.func = LOCK_DATA_XB;
- LP.handle = passedHandle;
- LP.dlMode = LOCK_SHARED;
- LP.startRec = 0; // lock entire data file
- rez = BULLET(&LP);
- if (rez) goto ErrorHandler;
-
- SDP.func = STAT_DATA_XB;
- SDP.handle = passedHandle;
- rez = BULLET(&SDP);
- if (rez) goto ErrorHandler; // must unlock file in handler
-
- DeriveSuitableName(SDP.filenamePtr,BackupDataName);
-
- CP.func = BACKUP_FILE_XB;
- CP.handle = passedHandle; // set to -passedHandle to skip memo backup
- CP.filenamePtr = BackupDataName;
- rez = BULLET(&CP);
- if (rez) goto ErrorHandler; // must unlock file in handler
-
- // unlocking also flushes the data header if not LOCK_SHARED (and if required)
-
- LP.func = UNLOCK_DATA_XB;
- LP.handle = passedHandle;
- rez = BULLET(&LP);
- if (rez) goto ErrorHandler;
- }
- }
-
-
- ΓòÉΓòÉΓòÉ 10.5. Get Error Class ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- XERRORPACK XEP; // packs used here
-
- // Bullet errors range from 8192 to 8999, any other return code
- // indicates the error number was generated by OS/2 itself
-
- if (rez < 8192) | (rez > 8999) {
- XEP.func = GET_ERROR_CLASS_XB;
- XEP.stat = rez;
- rez = BULLET(&XEP);
-
- // here XEP.errClass, .action, and .location are set
- // this call is the same as OS/2 API DosErrClass()
-
-
- ΓòÉΓòÉΓòÉ 10.6. Query System Variables ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- QUERYSETPACK QSP; // packs used here
-
- // Query a Bullet system variable
-
- QSP.func = QUERY_SYSVARS_XB;
- QSP.item = MUTEX_SEM_HANDLE; // in bullet2.h
- rez = BULLET(&QSP);
- if (rez==0)
- printf("Bullet/2 mutex handle is %d\n",QSP.itemValue);
-
-
- ΓòÉΓòÉΓòÉ 10.7. Set System Variables ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- QUERYSETPACK QSP; // packs used here
-
- // Set a Bullet system variable
-
- QSP.func = SET_SYSVARS_XB;
- QSP.item = REINDEX_BUFFER_SIZE; // in bullet2.h
- QSP.itemValue = 384*1024; // set to 384KB
- rez = BULLET(&QSP);
- if (rez==0) {
- printf("Reindex buffer sized changed to 384K.\n");
- printf("Previous setting was %d.\n",QSP.itemValue);
-
- // For REINDEX_BUFFER_SIZE, a value of 0 represents 'autosize', for which
- // Bullet selects a minimum usuable size (often 144KB). The minimum size
- // that you can use for REINDEX_BUFFER_SIZE is 48KB.
-
-
- ΓòÉΓòÉΓòÉ 10.8. Set Dual-video Monitor ΓòÉΓòÉΓòÉ
-
-
- // SET_DVMON_XB is not used in OS/2
-
-
- ΓòÉΓòÉΓòÉ 10.9. Create, Open, and Close Data and Index Files ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- CREATEDATAPACK CDP;
- CREATEINDEXPACK CIP;
- OPENPACK OP;
- HANDLEPACK HP; // packs used here
-
- // create the data file
-
- #pragma pack(1) // ensure compiler does not pad Bullet-used structures
- // (not needed here since all members are CHAR)
-
- // generally, only left-justified strings should be 0-terminated, numbers or
- // date fields should not be 0-terminated
-
- typedef struct _RECTYPE {
- CHAR tag; // record tag, init to SPACE, '*' means deleted
- CHAR userSSN[9]; // first field in DBF (not 0-terminated in this case)
- CHAR userScore[6]; // second field (also not 0T)
- } RECTYPE; // (total record length is 16 bytes)
- RECTYPE ourRecord;
-
- #pragma pack()
-
- CHAR nameIX3[] = "INDEX.IX3"; // index pathname
- CHAR keyExpression[] = "SSN"; // key is built from field named 'SSN'
- ULONG indexID=0; // handle of opened index file
- CHAR keyBuffer[68]; // buffer to store/receive key values
-
- CHAR nameData[] = "DATA.DBF"; // data pathname
- ULONG dataID=0; // handle of opened data file
- FIELDDESCTYPE fieldList[2]; // 2 fields used in data record (SSN and SCORE)
-
- // the field descriptor info must have unused entries set to 0
-
- memset(fieldList,0,sizeof(fieldList)); // init unused bytes to 0 (required)
-
- // fill in the field descriptor info for the data file you want to create
- // this descriptor must match the layout of ourRecord, above (ourRecord.tag
- // is implicit and is not a formal field, and so is not in fieldList[])
-
- strcpy(fieldList[0].fieldName, "SSN"); // field names must be upper-case
- fieldList[0].fieldType = 'C'; // field types must be upper-case
- fieldList[0].fieldLen = 9; // * Note that the .fieldname here
- fieldList[0].fieldDC = 0; // * matches the keyExpression
-
- strcpy(fieldList[1].fieldName, "SCORE");
- fieldList[1].fieldType = 'C';
- fieldList[1].fieldLen = 6; // 6 is total size of field
- fieldList[1].fieldDC = 0;
-
- // Create the data file as defined in fieldList above
- // To create only a DBF, set CDP.fileID=3
- // To create both a DBF and a DBT memo file, set CDP.fileID=0x8B
-
- CDP.func = CREATE_DATA_XB;
- CDP.filenamePtr = nameData;
- CDP.noFields = 2;
- CDP.fieldListPtr = fieldList;
- CDP.fileID = 3;
- rez = BULLET(&CDP);
- if (rez) {
- printf("Failed data file create. Err: %d\n",rez);
- return(rez);
- }
-
- // Open the data file (required before creating an index file for it)
-
- OP.func = OPEN_DATA_XB;
- OP.filenamePtr = nameData;
- OP.asMode = READWRITE | DENYNONE;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed data file open. Err: %d\n",rez);
- return(rez);
- }
- dataID = OP.handle;
-
- // Create an index file for the data file opened above.
- // This example uses a simple primary key: the SSN field.
- // Since it is assumed to be unique, DUPS_ALLOWED is not
- // OR'ed with the .sortFunction.
-
- CIP.func = CREATE_INDEX_XB;
- CIP.filenamePtr = nameIX3;
- CIP.keyExpPtr = keyExpression;
- CIP.xbLink = dataID; // the handle of the data file
- CIP.sortFunction = ASCII_SORT; // sort key by ASCII (fine for SSN ordering)
- CIP.codePage = 0; // use OS-default code page
- CIP.countryCode = 0; // use OS-default country code
- CIP.collatePtr = NULL; // no need for a special collate table
- CIP.nodeSize = 512; // 512-byte node size (or 1024, 2048 bytes)
- rez = BULLET(&CIP);
- if (rez) {
- printf("Failed index file create. Err: %d\n",rez);
- return(rez);
- }
-
- // Open the index file (what we just created above).
- // As with the index-create, the index-open requires the handle of the data
- // file which this index file indexes.
-
- OP.func = OPEN_INDEX_XB;
- OP.filenamePtr = nameIX3;
- OP.asMode = READWRITE | DENYNONE;
- OP.xbLink = dataID;
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed index file open. Err: %d\n",rez);
- return(rez);
- }
- indexID = OP.handle;
-
- // at this point, both the data and index files are open and accessible
-
- // |
- // | do work as required, then, when done, close them
- // |
-
- if (indexID) {
- HP.func = CLOSE_INDEX_XB;
- HP.handle = indexID;
- rez = BULLET(&HP);
- if (rez)
- printf("Failed index file close. Err: %d\n",rez);
- }
-
- if (dataID) {
- HP.func = CLOSE_DATA_XB;
- HP.handle = dataID;
- rez = BULLET(&HP);
- if (rez)
- printf("Failed data file close. Err: %d\n",rez);
- }
-
-
- ΓòÉΓòÉΓòÉ 10.10. Read Data and Index Header ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- HANDLEPACK HP; // packs used here
-
- // Since it is recommended that a full-lock be in force before reloading
- // data or index headers, and since performing a full lock from BULLET does
- // itself reload the header, this routine would not normally be used.
- // However, if you are doing your own locking, then you need to call this.
-
- rez = YourExternalControlLockRoutine; // so you have your own locks...
-
- if (rez==0) {
-
- if (youWantDataReload) {
- HP.func = READ_DATA_HEADER_XB;
- HP.func = dataID;
- }
- else {
- HP.func = READ_INDEX_HEADER_XB;
- HP.func = indexID;
- }
- rez = BULLET(&HP);
-
- // since locked, release lock regardless rez value
-
- rez2 = YourExternalControlUnlockRoutine;
- }
-
- // Be sure to read the explanation above
-
-
- ΓòÉΓòÉΓòÉ 10.11. Flush Data and Index Header ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- HANDLEPACK HP; // packs used here
-
- // While BULLET automatically flushes data and index info whenever
- // a BULLET file is unlocked from a full-lock (and if it
- // is needed), you may to flush more frequently.
-
- // It is recommended that you have a full-lock on the file before
- // flushing. In normal use, you would always have a full-lock when
- // writing to a file (the only time you need to flush is if the file
- // has been written to). If you are, for whatever reason, keeping
- // the file locked, and are updating it repeatedly, and have no
- // intention of unlocking (and thereby flushing it) any time soon,
- // you may do a manual flush to ensure that the disk image matches
- // the memory image (in case the power goes out and you have no UPS)
-
- if (youWantDataFlush) {
- HP.func = FLUSH_DATA_HEADER_XB;
- HP.func = dataID;
- }
- else {
- HP.func = FLUSH_INDEX_HEADER_XB;
- HP.func = indexID;
- }
- rez = BULLET(&HP);
-
- // Only if the file has been changed does a flush actually write to disk.
- // You should have an exclusive full-lock; a shared full-lock cannot be
- // used since a shared lock does not allow writing to the file.
-
-
- ΓòÉΓòÉΓòÉ 10.12. Copy (Add) a Subset of Records to a New File ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK APo; // used when accessing original
- ACCESSPACK APn; // used when accessing new
- COPYPACK CP;
- HANDLEPACK HP;
- OPENPACK OP; // packs used here
-
- RECTYPE ourRecord; // as defined in Create example
-
- // Assume an open, locked DBF data file, handle in dataID, and that you
- // want to copy those records that are marked as deleted, one at a time, to a
- // new file, prior to packing the database. An extension to this procedure is
- // to access each record in key order, and to copy each record to the new
- // file, thereby giving a SORTED data file (also known as a clustered file).
- // The lock must be a full-lock, but may be a shared full-lock. A ZAP is done
- // on the new file if the copy did not complete as expected.
-
- // No indexed access is used in this example.
-
- CP.func = COPY_DATA_HEADER_XB;
- CP.handle = dataID; // dataID is the original data file handle
- CP.filenamePtr = "DELRECS.DBF"; // new file for deleted records
- rez = BULLET(&CP);
- if (rez) {
- printf("Failed header copy. Err: %d\n",rez);
- return(rez);
- }
-
- // DELRECS.DBF now is exactly like the original, but has 0 records. Since
- // we're building this file, open it for exclusive use (DENYREADWRTE) --
- // this is different from using the LOCK_XB routines since the lock is done
- // at the file open level (no other process may even open it), whereas
- // the LOCK_XB routines are at the access level (other processes may
- // open the file, but may or may not access it). You could instead use
- // LOCK_DATA_XB for an exclusive full-lock, but this is simpler.
-
- OP.func = OPEN_DATA_XB;
- OP.filenamePtr = "DELRECS.DBF"; // open it
- OP.asMode = READWRITE | DENYREADWRITE; // for exclusive use
- rez = BULLET(&OP);
- if (rez) {
- printf("Failed new file open. Err: %d\n",rez);
- return(rez);
- }
-
- // Now have two files open: the original, with all the records, and
- // the new, with no records. The procedure here is to get all original
- // records, check each for being deleted, if so, add that record (copy it)
- // to the new file. After copying, the original file is packed so that
- // all deleted records are removed. Then an index to it is reindexed.
-
- // most BULLET pack members will be invariant within loops... (low overhead!)
-
- APn.func = ADD_RECORD_XB; // AccessPack for the new file
- APn.handle = OP.handle; // the handle just opened above
- APn.recPtr = &ourRecord;
-
- APo.func = GET_RECORD_XB; // AccessPack for the original file
- APo.handle = dataID;
- APo.recNo = 1; // start at the first record
- APo.recPtr = &ourRecord; // read data into this
- rez = BULLET(&APo); // read the first data record
-
- while (rez==0) {
-
- // check if this record is deleted, if so copy it else continue
-
- if (ourRecord.tag = '*') {
- rez = BULLET(&APn); // add it to the new file
- if (rez) break;
- // here APn.recNo is the record number used for the just-added record
- }
-
- // This while() loop could have been a for() loop if we had used
- // STAT_DATA_XB to get the number of records, but in this example,
- // the original file is read until EXB_BAD_RECNO is returned,
- // indicating that the last record has been passed.
-
- APo.recNo++; // get next original record
- rez = BULLET(&APo); // everything else is already setup
- }
-
- // the expected rez here is EXB_BAD_RECNO, any other then quit
-
- if (rez != EXB_BAD_RECNO) {
- printf("Failed the while() loop, ZAPing new file. Err: %d\n",rez);
-
- // As an example, if the copy failed to complete as expected, the
- // new file is ZAP'ed, removing any records that may have been copied
- // up to the point that the error occurred -- investigate failure and
- // restart process (you may want to just delete, rather than ZAP/CLOSE).
-
- HP.func = ZAP_DATA_HEADER_XB;
- HP.handle = APn.handle; // the new file handle
- rez2 = BULLET(&HP); // using 'rez2' to preserve initial error code
- if (rez2)
- printf("Failed ZAP! Err: %d\n",rez2);
-
- HP.func = CLOSE_DATA_XB; // close
- rez2 = BULLET(&HP);
- if (rez2)
- printf("Failed CLOSE! Err: %d\n",rez2);
-
- return(rez); // return initial error code
- }
-
- // done with the new file
- // it contains all the records in the original marked as deleted
-
- HP.func = CLOSE_DATA_XB; // always use the correct pack for the routine
- HP.handle = APn.handle; // -- do not try to use AP when closing a file
- rez = BULLET(&HP);
- if (rez) return(rez);
-
- // Pack the original data file, now that we've "saved" the delete-marked recs.
- // Note: You may want to use BACKUP_XB before using this next routine
- // in case a serious error occurs. BULLET packs in place! Also, before
- // packing, memos belonging to records about to be deleted should have
- // have been deleted before packing. Neither is shown here.
-
- APo.func = PACK_RECORDS_XB;
- rez = BULLET(&APo);
- if (rez==0) {
-
- // After a pack, you must reindex any related index files.
- // Assume here one index file, handle in indexID
-
- APo.func = REINDEX_XB;
- APo.handle = indexID;
- rez = BULLET(&APo);
- if (rez)
- printf("Failed reindex. Err: %d\n",rez);
- }
- else {
- printf("Failed packed! Recommend restore from backup. Err: %d\n",rez);
- printf("Note: Unless error is known to not be severe.\n");
- }
-
- // Here and rez==0 then everything went as planned.
-
- return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.13. Zap Index File ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- HANDLEPACK HP; // packs used here
-
- // Since BULLET reindexes in place, there's typically no need to use
- // ZAP to reduce disk requirements (there won't be two separate file
- // spaces since the reindex copies right over the old key data).
-
- HP.func = ZAP_INDEX_HEADER_XB;
- HP.handle = indexToZap;
- rez = BULLET(&HP);
- if (rez) return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.14. Get Descriptor, Using a DBF Not Created by Bullet ΓòÉΓòÉΓòÉ
-
- #include <os2.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "bullet2.h"
-
- int main(int argc,char *argv[]) {
-
- INITPACK IP;
- EXITPACK EP;
- DESCRIPTORPACK DP;
- OPENPACK OP;
- HANDLEPACK HP;
- STATDATAPACK SDP;
- ACCESSPACK AP; // packs used here
-
- PFIELDDESCTYPE fieldDescPtr; // pointer to field descriptor base allocation
- PFIELDDESCTYPE fdPtr; // roving pointer to any field's descriptor
-
- CHAR dataRec[8192]; // 'unknown' record layout since reading "any" DBF
- CHAR fmt[32]; // printf() fmt string for on-the-fly formatting
- ULONG dataID; // handle of DBF
- LONG recNo; // loop counter
- BYTE fldNo; // loop counter2
- int rez; // primary op return code
- int rez2; // secondary op return code (so to perserve primary rc)
-
- if (argc < 2) {
- puts("Use: C>progname anyfile.dbf");
- return(1);
- }
-
- // init Bullet
-
- IP.func = INIT_XB;
- IP.JFTsize = 20; // 20 handles is all we need here
- rez = BULLET(&IP);
- if (rez!=0) {
- printf("INIT_XB failed: %d\n",rez);
- return(1);
- }
-
- // open existing DBF as named in command line
-
- OP.func = OPEN_DATA_XB;
- OP.filenamePtr = argv[1];
- OP.asMode = READWRITE | DENYNONE;
- rez = BULLET(&OP);
- if (rez==0) {
-
- dataID = OP.handle;
-
- SDP.func = STAT_DATA_XB;
- SDP.handle = dataID;
- rez = BULLET(&SDP);
- if (rez==0) {
-
- // allocate field descriptors needed (SDP.fields is number needed)
- // calloc() used since 0-filled storage is required
-
- fieldDescPtr = calloc(SDP.fields,sizeof(FIELDDESCTYPE));
-
- if (fieldDescPtr != NULL) {
-
- fdPtr = fieldDescPtr; // fdPtr->each descriptor
-
- // read each field descriptor from Bullet, storing to our program
- // show each for display
-
- // 1234567890-123456789-123456789-12345
- printf("FLD# FIELDNAME T LEN.DEC OFFSET\n");
-
- DP.func = GET_DESCRIPTOR_XB;
- DP.handle = dataID;
- for (fldNo=1;fldNo <= SDP.fields;fldNo++) {
-
- DP.fieldNumber = fldNo;
- rez = BULLET(&DP);
- if (rez==0) {
-
- strcpy(fdPtr->fieldName, DP.FD.fieldName);
- fdPtr->fieldType = DP.FD.fieldType;
- fdPtr->fieldLen = DP.FD.fieldLen;
- fdPtr->fieldDC = DP.FD.fieldDC;
- fdPtr->fieldDA = DP.fieldOffset;
- printf("%3u %-10s %c %3u.%1u %4u\n",
- fldNo,
- fdPtr->fieldName,
- fdPtr->fieldType,
- (ULONG) fdPtr->fieldLen,
- (ULONG) fdPtr->fieldDC,
- fdPtr->fieldDA);
- fdPtr++; // next field descriptor
- }
- else
- break;
- }
-
- // An interesting item above is where fdPtr->fieldDA is set to
- // DP.fieldOffset. fieldDA is a run-time storage area that in
- // dBASE is used to directly access the field (DA="direct access").
- // It has no meaning except for that particular run (it is a memory
- // address). In this program example I use it to store the offset
- // of the field, relative the start of the record buffer (where the
- // tag byte = offset 0). You could just as easily use some of the
- // 12 reserved bytes left over in the descriptor, as I do for the
- // alternate field length. But, since fieldDA is already there, and
- // not used otherwise, it makes sense to use it.
-
- // Now have all we need to know about the DBF fields, having just
- // read and stored the field descriptors. For this example, we
- // grab the first nine records and spit them out, by field, in record
- // number order (no indexing used).
-
- if (SDP.records != 0) {
-
- AP.func = GET_RECORD_XB;
- AP.handle = dataID;
- AP.recPtr = &dataRec;
-
- for (recNo=1;recNo <= 9; recNo++) {
-
- printf("\nrecNo %u: ",recNo); // show line number
- AP.recNo = recNo; // get this record #...
- rez = BULLET(&AP); // ...to dataRec buffer
- if (rez==0) {
- printf("%.1s ",(CHAR *) dataRec); // show if deleted or not
-
- fdPtr = fieldDescPtr; // fdPtr->first field descriptor
-
- for (fldNo=1;fldNo <= SDP.fields;fldNo++) {
-
- // No special formatting is done on this output for this
- // example -- since standard DBF data is always in pure
- // ASCII form, all is printable.
-
- switch (fdPtr->fieldType) {
- case 'C': // text
- case 'D': // date, show as-is
- case 'L': // logical, show as-is
- case 'M': // memo field (block number in ASCII)
- case 'N': // numeric (ASCII)
-
- // make fmt[] string like this: "%xx.xxs"
- // where xx is field length for this field
-
- sprintf(fmt,"%%-%i.%is ",
- fdPtr->fieldLen,
- fdPtr->fieldLen);
-
- // fdPtr->fieldDA=offset of the field within the record
- // so it plus dataRec (buffer base) results in the
- // offset of the current field we are processing
-
- printf(fmt,dataRec+fdPtr->fieldDA);
- break;
- default:
- printf("\nUnknown field type: %c\n",fdPtr->fieldType);
- } // switch
-
- fdPtr += 1; // next field's descriptor
-
- } // for fields
- } // if record read
-
- else {
- if (rez==EXB_BAD_RECNO) // if < for-count records in DBF
- rez=0; // then would get this error
- else
- printf("Failed GET_RECORD_XB, err: %d\n",rez);
- break; // break for any ELSE case
- }
-
- } // for records
- if (rez==0) printf("\nDone.\n"); // all FOR recs done
- }
- else
- printf("No records in file\n");
-
- free(fieldDescPtr);
- }
- else
- printf("calloc failed!\n");
- }
- else
- printf("STAT_DATA_XB failed: %d\n",rez);
-
- HP.func = CLOSE_DATA_XB;
- HP.handle = dataID;
- rez2 = BULLET(&HP);
- }
- else
- printf("OPEN_DATA_XB failed: %d\n",rez);
-
- EP.func = EXIT_XB;
- rez2=BULLET(&EP);
-
- printf("\nPress ENTER to exit");
- getchar();
- if (rez==0) rez=rez2; // rez is more important, but if 0 use rez2 result
- return(rez);
- }
-
- The above is a complete program. Running it against a sample DBF results in
- the following output:
-
- FLD# FIELDNAME T LEN.DEC OFFSET
- 1 SSN C 9.0 1
- 2 LNAME C 16.0 10
- 3 FNAME C 16.0 26
- 4 HIRED D 8.0 42
- 5 DEPT_ID C 6.0 50
-
- recNo 1: 465309999 Que Barbie 19900131 BOSS
- recNo 2: 445038888 Stewart Jackie 19910228 ACC
- recNo 3: 760443232 Whitman Kelly 19920414 HUM
- recNo 4: 845309944 Beatty Leslie 19940122 PRG
- recNo 5: 555033388 Jasper Amy 19930230 PRG
- recNo 6: 430443222 Hauntos Poco 19920414 PRG
- recNo 7: 365502949 Hopkins Lisa 19910121 PRG
- recNo 8: 685733868 Leonard Rosina 19850218 PRG
- recNo 9: 500945242 Morton Holly 19950406 PHY
- Done.
-
- Press ENTER to exit
-
-
- ΓòÉΓòÉΓòÉ 10.15. Update a Data Record ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP; // packs used here
-
- typedef struct _RECTYPE {
- CHAR tag; // record tag, init to SPACE, '*' means deleted
- CHAR userSSN[9]; // first field in DBF (not 0-terminated in this case)
- CHAR userScore[6]; // second field (also not 0T)
- } RECTYPE; // (total record length is 16 bytes)
- RECTYPE ourRecord;
-
- // This excerpt demonstrates how to update (change) a record.
- // The idea is to get the current contents of a record (by record
- // number since no index is used for this update routine), change
- // what needs changing, then write it back. Under no circumstances
- // should you change any field that is used as a key by an index, or
- // as a foreign key, or in any other way removes the referential
- // integrity of the database. If you need to change a key field, then
- // you must use UPDATE_XB.
-
- AP.func = GET_RECORD_XB;
- AP.handle = dataID;
- AP.recNo = recordToUpdate;
- AP.recPtr = &ourRecord;
- rez = BULLET(&AP);
- if (rez) return(rez);
-
- // ourRecord has data stored at record number recordToUpdate -- since
- // userScore is not used as a key field, this routine may be used to
- // modify the contents of that field. Since numbers are stored as ASCII
- // text in compatible DBF files, must convert to binary, perform needed
- // math, then convert back to text:
-
- t = atol(ourRecord.userScore);
- t = t + ClassCurve; // increase each score by curve value
- sprintf(ourRecord.userScore,"%6.6u",t)
-
- AP.func = UPDATE_RECORD_XB; // other AP values already set up from GET
- rez = BULLET(&AP); // write out the record with the new score
- if (rez) return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.16. Delete, Undelete, 'Debump' a Data Record ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP; // packs used here
-
- // delete or undelete or remove (debump)
-
- switch(*requestMsg) {
- case 'delete':
- AP.func = DELETE_RECORD_XB; // places a '*' in .tag byte
- break;
- case 'undelete':
- AP.func = UNDELETE_RECORD_XB; // places a SPACE in .tag byte
- break;
- case 'debump':
- AP.func = DEBUMP_RECORD_XB; // physically removes record from file, but
- break; // fails if AP.recNo is not last record number
- }
-
- AP.handle = dataID; // same for all three
- AP.recNo = recordNumber;
- rez = BULLET(&AP);
- return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.17. Reading Memo Records ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP;
- LOCKPACK LP;
- MEMODATAPACK MDP; // packs used here
-
- typedef struct _RECTYPE {
- CHAR tag; // record tag, init to SPACE, '*' means deleted
- CHAR userSSN[9]; // first field in DBF (not 0-terminated in this case)
- CHAR userMemo[10]; // second field (also not 0T), is memo field type
- } RECTYPE;
- RECTYPE someRecord;
-
- // a minimum shared record lock is required on the DBF record that owns
- // memoNumber in this example, since only reading of the single memo is done
-
- LP.func = LOCK_DATA_XB;
- LP.handle = dataID;
- LP.dlMode = LOCK_SHARED; // only reading here, shared record lock is okay
- LP.recStart = recordToGet; // lock this record
- LP.recCount = 1; // and only this record
- rez = BULLET(&LP);
- if (rez) return(rez);
-
- // hereafter, must unlock before exiting
-
- AP.func = GET_RECORD_XB;
- AP.handle = dataID;
- AP.recNo = recordToGet;
- AP.recPtr = &someRecord; // load someRecord with data on disk
- rez = BULLET(&AP);
- if (rez) return(rez); // UNLOCK! before doing this exit
-
- memoNumber = atol(someRecord.userMemo); // the memo number (0 if none)
-
- // this code reads the number of data bytes in the memo and allocates a
- // run-time buffer to read that memo into
-
- MDP.func = GET_MEMO_SIZE_XB;
- MDP.dbfHandle = dataID; // handle of the DBF this memo belongs to
- MDP.memoNo = memoNumber; // memo number to get size of (1=first)
- rez = BULLET(&MDP); // (returns an error if memoNumber is 0)
- if (rez==0) {
-
- // BULLET does maintain 0-sized memo records, so you may want to check
- // if MDP.memoBytes from GET_MEMO_SIZE_XB is 0, and skip processing if so
-
- memoBytesToRead = MDP.memoBytes; // since overwritten by next call
- memoBufferPtr = malloc(memoBytesToRead); // assuming you want it all at once
-
- if (memoBufferPtr) {
-
- MDP.func = GET_MEMO_XB;
- MDP.dbfHandle = dataID; // same as before, as is .memoNo
- MDP.memoNo = memoNumber;
- MDP.memoPtr = memoBufferPtr; // memo disk data is loaded into this buffer
- MDP.memoOffset = 0; // read from very first memo data byte
- MDP.memoBytes = memoBytesToRead;
-
- // MDP.memoBytes is already set to the total data size -- you may read
- // fewer bytes, and you may use .offset to move through the memo data
- // chunks at a time, rather than all at once -- the above simply reloads
- // it with the same count, since here memoBytesToRead==MDP.memoBytes
-
- rez = BULLET(&MDP); // returns with MDP.memoBytes= bytes read
- if (rez==0) {
-
- // if (MDP.memoBytes != memoBytesToRead)
- // printf("Could not read all bytes requested - probably at end of memo\n");
- // above would not happen in this case since the exact size was requested
-
- // process as required (here passes buffer ptr and bytes actually read)
-
- DoWhatYouWillWithThisMemoData(memoBufferPtr,MDP.memoBytes);
- }
- free(memoBufferPtr);
- }
- else rez=8; // malloc failed, return 8=not enough memory
- }
-
- LP.func = UNLOCK_DATA_XB;
- LP.handle = dataID;
- LP.recStart = recordToGet; // unlock this record
- LP.recCount = 1; // and only this record
- rez2 = BULLET(&LP);
-
- if (rez==0) rez=rez2;
- return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.18. Add, Update, Delete a Memo Record ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP;
- LOCKPACK LP;
- MEMODATAPACK MDP; // packs used here
-
- typedef struct _RECTYPE {
- CHAR tag;
- CHAR userSSN[9];
- CHAR userMemo[10];
- } RECTYPE;
- RECTYPE someRecord;
-
- // an exclusive lock is required on the DBF file that owns this memo file
- // since writing is done to the memo file (memo file header, especially)
-
- LP.func = LOCK_DATA_XB;
- LP.handle = dataID;
- LP.dlMode = LOCK_EXCLUSIVE; // writing here, exclusive full lock required
- LP.recStart = 0; // lock all
- rez = BULLET(&LP);
- if (rez) return(rez);
-
- // hereafter, must unlock before exiting
-
- AP.func = GET_RECORD_XB;
- AP.handle = dataID;
- AP.recNo = recordToGet;
- AP.recPtr = &someRecord; // load someRecord with data on disk
- rez = BULLET(&AP);
- if (rez) return(rez); // UNLOCK! before doing this exit
-
- memoNumber = atol(someRecord.userMemo); // the memo number (0 if none)
-
- // if there is no current memo, this example adds one
- // if there is, this example updates it by:
- // - changing the first 16 bytes to "Kilroy was here."
- // - adding the text "Was updated." to the end of the current memo data
- // the example then deletes the memo
-
- CHAR kilroyStr[] = "Kilroy was here.";
- CHAR updateStr[] = "Was updated.";
- CHAR newStr[] = "New.";
-
- if (memoNumber) {
-
- // modify the current memo -- first, the text "Kilroy was here." is
- // placed at the start of the memo, then the memo size is gotten (in
- // case the original memo size were less than the size of "Kilroy...").
-
- MDP.func = UPDATE_MEMO_XB;
- MDP.dbfHandle = dataID; // handle of the DBF this memo belongs to
- MDP.memoNo = memoNumber; // memo number to update (1=first)
- MDP.memoPtr = kilroyStr; // data to write
- MDP.memoOffset = 0; // start write at first byte of memo
- MDP.memoBytes = strlen(kilroyStr); // bytes to write
- rez = BULLET(&MDP);
-
- // the first 16 bytes of the memo now say kilroyStr (overwrote what was there)
- // the memo size changes only if the original memo was < 16 bytes, in
- // which case the size is now 16
-
- if (rez==0) {
- printf("%s overwrote first 16 bytes\n",kilroyStr);
-
- // must check if the update resulted in a new memo block being
- // used (if the update required more allocation blocks), and if
- // so must update the DBF field storing the memo number
- // MDP.memoNo is the memo number returned, memoNumber the original number
-
- // IT CAN BE ASSUMED IN THIS PARTICULAR example that this will never
- // be needed since the update modified the first 16 bytes of the
- // memo only, and so would never have required any more blocks --
- // however, unless you know before-hand that the update will not need
- // more allocation blocks (not difficult, if you know the block size,
- // overhead bytes (8), and your offset and bytes to write), it should
- // be checked -- hint: as with all Bullet routines, the idea is to
- // wrap up these separate operations into nice, neat callable routines
- // that take care of your particular need; there are no doubt 1000s of
- // variations -- pick one you can deal with.
-
- if (MDP.memoNo != memoNumber) {
- sprintf(someRecord.userMemo,"%10.10u",MDP.memoNo)
- memoNumber = MDP.memoNo; // set original to new for next update
-
- // rather than updating here, and possibly again below, you
- // may elect to set a flag and then do the DBF update at the end, once
- // again -- this updates the _data_ record, in the DBF file:
-
- AP.func = UPDATE_RECORD_XB; // this updates the data record only
- AP.handle = dataID;
- AP.recNo = recordToGet;
- AP.recPtr = &someRecord; // write the new data
- rez = BULLET(&AP);
- if (rez) return(rez); // UNLOCK! before doing this exit
- }
-
- MDP.func = GET_MEMO_SIZE_XB; // other MDP members already set above
- rez = BULLET(&MDP);
- if (rez=0) { // size of memo is at least 16 (from kilroyStr)
- // but may be bigger if was bigger before
- MDP.func = UPDATE_MEMO_XB;
- MDP.memoPtr = updateStr;
-
- // the current memo size is used as the offset for the appending of
- // the updateStr bytes (offset is 0-based, so using MDP.memoBytes
- // results in the offset being the current size + 1)
-
- MDP.memoOffset = MDP.memoBytes;
- MDP.memoBytes = strlen(updateStr);
- rez = BULLET(&MDP); // returns with MDP.memoNo
- // bytes written==MDP.memoBytes always
- if (rez==0)
-
- printf("%s appended to memo\n",updateStr);
- // _minimum_ memo contents now is kilroyStr plus updateStr
- // more if original memo was > 16 bytes
-
- if (MDP.memoNo != memoNumber) {
- sprintf(someRecord.userMemo,"%10.10u",MDP.memoNo)
- AP.func = UPDATE_RECORD_XB; //
- AP.handle = dataID; // as explained above
- AP.recNo = recordToGet; //
- AP.recPtr = &someRecord;
- rez = BULLET(&AP);
- if (rez) return(rez); // actually must unlock before exit!
- }
- } // memo update #2 failed
- } // memo size failed
- }
- else
- printf("update failed, err: %d\n",rez); // disk full probably
- }
- else {
-
- // no current memo, add one
-
- MDP.func = ADD_MEMO_XB;
- MDP.dbfHandle = dataID; // handle of the DBF this memo belongs to
- MDP.memoPtr = newStr; // data to write
- MDP.memoBytes = strlen(newStr); // bytes to write
- rez = BULLET(&MDP);
- if (rez==0) {
- sprintf(someRecord.userMemo,"%10.10u",MDP.memoNo)
- AP.func = UPDATE_RECORD_XB; //
- AP.handle = dataID; // as explained above
- AP.recNo = recordToGet; //
- AP.recPtr = &someRecord;
- rez = BULLET(&AP);
- }
- else
- printf("add failed, err: %d\n",rez);
- }
-
- // delete the memo just operated on
-
- if (rez==0) {
-
- MDP.func = DELETE_MEMO_XB;
- // MDP.memoNo is already set
- rez = BULLET(&MDP);
- if (rez==0) {
- strcpy(someRecord.userMemo," ");// DBF memo field to all spaces
- AP.func = UPDATE_RECORD_XB; //
- AP.handle = dataID; // as explained above
- AP.recNo = recordToGet; //
- AP.recPtr = &someRecord;
- rez = BULLET(&AP);
- }
- else
- printf("delete failed, err: %d\n",rez);
- }
-
- LP.func = UNLOCK_DATA_XB;
- LP.handle = dataID;
- LP.recStart = 0; // unlock all
- rez2 = BULLET(&LP);
-
- if (rez==0) rez=rez2;
- return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.19. Memo Bypass (Memo Create, Open, Close, Read/Flush Header) ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- MEMODATAPACK MDP; // packs used here
-
-
- // These five routines are normally performed automatically, as described
- // in the main documentation, and seldom would need to be called directly.
-
-
- MDP.func = MEMO_BYPASS_XB;
- MDP.dbfHandle = dataID; // handle of DBF
- MDP.memoBypass = BypassRoutineToDo;
-
- // where BypassRoutineToDo is one of the following:
- //
- // BYPASS_CREATE_MEMO
- // BYPASS_OPEN_MEMO
- // BYPASS_CLOSE_MEMO
- // BYPASS_READ_MEMO_HEADER
- // BYPASS_FLUSH_MEMO_HEADER
-
- rez = BULLET(&MDP);
- if (rez) return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.20. Key Access Without Data Record Read ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- CHAR keyBuffer[64]; // enough for the largest possible key
-
- ACCESSPACK AP; // packs used here
-
- // -----------------------------------------------------------
- // this section starts at the first in-order key and reads all
- // keys in the index file in order
-
- AP.func = FIRST_KEY_XB;
- AP.handle = indexID;
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
- while (rez==0) {
-
- // show first 8 bytes of key, and the record number the key is for
- printf("%8.8s %9.9lu\r", keyBuffer, AP.recNo);
-
- AP.func = NEXT_KEY_XB; // and get the next key...
- rez = BULLET(&AP); // until all keys accessed
- };
- if (rez!=EXB_END_OF_FILE) return(rez); // expected rez is EXB_END_OF_FILE
-
-
- // ----------------------------------------------------------
- // this section starts at the last in-order key and reads all
- // keys in the index file in reverse order
-
- AP.func = LAST_KEY_XB;
- AP.handle = indexID;
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
- while (rez==0) {
-
- // show first 8 bytes of key, and the record number the key is for
- printf("%8.8s %9.9lu\r", keyBuffer, AP.recNo);
-
- AP.func = PREV_KEY_XB; // and get the previous key...
- rez = BULLET(&AP); // until all keys accessed
- };
- if (rez!=EXB_TOP_OF_FILE) return(rez); // expected rez is EXB_TOP_OF_FILE
-
- // this section starts at the first key that starts with "SM", or if
- // no keys do, the first key after "SM", and gets that key
- // keys in the index file in reverse order
-
- memset(keyBuffer,0,64); // ensure remaining bytes are \0 (required)
- strcpy(keyBuffer,"SM"); // get key of "SM", if present (not likely)
- AP.func = EQUAL_KEY_XB;
- AP.handle = indexID;
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
-
- // since "SM" is only being used as a partial key search criterion,
- // it won't be found (though, of course, it is a valid key) in this
- // example -- however, by using NEXT_KEY_XB, the next key is accessed,
- // say, for example, "SMITH"... It could also be "TIMBU" if there were
- // no keys with values greater than "SM" and less than "TIMBU".
-
- // Be aware that if another thread in this process (repeat:
- // this process!) is accessing this index file (it's not likely that
- // you will write your could so that this would happen), then you
- // can no longer rely on any multi-call key access to be an atomic
- // operation. If you need to have more than one thread access the
- // same index (and you require NEXT_KEY_XB or PREV_KEY_XB), then you
- // must semaphore protect your code so you don't try to access the same
- // index file.
-
- if (rez!=EXB_KEY_NOT_FOUND) { // expected
- AP.func = NEXT_KEY_XB; // so get the first key after "SM"
- AP.handle = indexID;
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
- if (rez==0)
- printf("The first key >= SM is %s\n",AP.keyBuffer);
- }
-
-
- ΓòÉΓòÉΓòÉ 10.21. Building and Storing Raw Key ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP; // packs used here
-
- // this example shows a simple database insert process
- // INSERT_XB should be used instead since it does all this and then some
- // files should be locked (not shown)
-
- AP.func = ADD_RECORD_XB;
- AP.handle = dataID;
- AP.recPtr = &yourRecord;
- rez = BULLET(&AP);
- if (rez) return(rez);
-
- // AP.recNo is returned by Bullet and will be used later
-
- AP.func = BUILD_KEY_XB;
- AP.handle = indexID;
- AP.recPtr = &yourRecord;
- AP.keyPtr = keyBuffer; // CHAR keyBuffer[64];
- rez = BULLET(&AP);
- if (rez) return(rez);
-
- // keyBuffer filled with key to store
- // a \0\0 enumerator is attached if the index file was created with DUPS_ALLOWED
-
- AP.func = STORE_KEY_XB;
- AP.handle = indexID;
- // AP.recNo is already set from the ADD_RECORD_XB call
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
- if (rez) return(rez);
-
- // if no error, the key was inserted in the index file and the record
- // number was associated with that key -- next time you access that
- // key, the record number is returned (along with the key itself) --
- // and with that record number you access the data file
-
- // when using this routine, you must check the error for a EXB_KEY_EXISTS
- // error and if DUPS_ALLOWED, you must manage your own enumerator --
- // INSERT_XB is the only routine that does this automatically so unless
- // you have a real desire to manage this yourself (among other things)
- // use INSERT_XB instead of all this.
-
-
- ΓòÉΓòÉΓòÉ 10.22. Getting Current Key, Key for Rec/RecNo Pair, Deleting Key ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP;
- LOCKPACK LP; // packs used here
-
- AP.func = GET_CURRENT_KEY_XB;
- AP.handle = indexID;
- AP.keyPtr = keyBuffer; // current key placed here by Bullet
- rez = BULLET(&AP);
- if (rez) return(rez);
- printf("The last accessed key for indexID is in keyBuffer, including any enumerator\n");
-
- // This next example assumes that you are maintaining your own method of
- // transaction rollback, and are about to delete the last item added to the
- // database: in this case, the last data record is removed from the one
- // DBF data file, and the key for that record is removed from the index file
- // -- normally, you wouldn't do this, but if you have the need...
-
- rez = InsertToDatabaseHoweverYouDoIt(yourPtr);
-
- // Assume the above called succeeded, but you've decided, for whatever reason,
- // that you want to backout the insert... normally, if you used INSERT_XB to
- // insert a record/key into a database, you'd already have the record number
- // used AND the key used for the record (for each if more than one) -- but for
- // this example, assume that only the record number is known. Steps done
- // to remove the record and key for this would be:
- //
- // 1. Exclusive full-lock files
- // 2. Get the record data at the record number (GET_RECORD_XB)
- // 3. Get the key for this record/recNo pair (GET_KEY_FOR_RECORD_XB)
- // 4. Delete the key (DELETE_KEY_XB) (delete the key before deleting the record)
- // 5. Delete the record data (DEBUMP_RECORD_XB) (record must be last record in file)
- // 6. Unlock files
-
- // lock files being processed
-
- LP.func = LOCK_XB;
- LP.handle = indexID; // also locks indexID's owner (its DBF)
- LP.xlMode = LOCK_EXCLUSIVE; // exclusive lock for index
- LP.dlMode = LOCK_EXCLUSIVE; // exclusive lock for data
- LP.nextPtr = NULL; // only one pack
- rez = BULLET(&LP);
- if (rez) return(LP.stat); // rez for transaction-list is NOT the error
-
- // get actual data record for record number
-
- AP.func = GET_RECORD_XB;
- AP.handle = dataID;
- AP.recNo = recNoToDelete; // get this record to...
- AP.recPtr = &yourRecord; // ...this data record buffer (a structure var)
- rez = BULLET(&AP);
- if (rez) goto MustAlwaysUnlock;
-
- // now have the data record/data number pair --
- // it must be the last physical record in the data file -- if this cannot
- // be known, use STAT_DATA_XB to verify that recNoToDelete==SDP.records
-
- AP.func = GET_KEY_FOR_RECORD_XB;
- AP.handle = indexID;
- AP.recNo = recNoToDelete;
- AP.recPtr = &yourRecord;
- AP.keyPtr = keyBuffer; // CHAR keyBuffer[64]; key returned here
- rez = BULLET(&AP);
- if (rez) goto MustAlwaysUnlock;
-
- // now have the key (with any attached enumerator) in keyBuffer, delete it
-
- AP.func = DELETE_KEY_XB;
- AP.handle = indexID;
- AP.keyPtr = keyBuffer; // was set with key by GET_KEY_FOR_RECORD_XB
- rez = BULLET(&AP);
- if (rez) goto MustAlwaysUnlock;
-
- // and delete (physically remove) the data record
-
- AP.func = DEBUMP_RECORD_XB;
- AP.handle = dataID;
- AP.recNo = recNoToDelete;
- rez = BULLET(&AP);
- // if (rez) goto MustAlwaysUnlock;
-
- MustAlwaysUnlock: // unlock ALWAYS required if lock succeeded
-
- LP.func = UNLOCK_XB;
- LP.handle = indexID;
- LP.nextPtr = NULL;
- rez2 = BULLET(&LP);
- if (rez2) rez2=LP.stat; // rez for transaction-list is NOT the error
-
- if (rez==0) rez=rez2;
- return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.23. Get Data by Key Order ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP; // packs used here
-
- // First example starts at first in-order data and moves through to last
-
- AP.func = GET_FIRST_XB;
- AP.handle = indexID;
- AP.recPtr = &yourRecord; // struct yourStructure yourRecord;
- AP.keyPtr = keyBuffer; // CHAR keyBuffer[64];
- rez = BULLET(&AP);
- if (rez) return(rez);
- printf("The first in-order key is in keyBuffer and its record in yourRecord\n");
-
- AP.func = GET_NEXT_XB; // other parm same as set above
- while (rez==0) {
- rez = BULLET(&AP);
- if (rez) break;
- printf("The next in-order key is in keyBuffer and its record in yourRecord\n");
- }
- if (rez==EXB_END_OF_FILE) rez==0; // expected rez after end of file
- if (rez) return(rez);
-
-
- // Second example starts at last in-order data and moves through to first
- // note: since above already is past last, the call to GET_LAST_XB in
- // this example would not be necessary -- a call to GET_PREV_XB
- // could have been made directly -- however, it doesn't matter
-
- AP.func = GET_LAST_XB;
- AP.handle = indexID;
- AP.recPtr = &yourRecord;
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
- if (rez) return(rez);
- printf("The last in-order key is in keyBuffer and its record in yourRecord\n");
-
- AP.func = GET_PREV_XB; // other parms same as set above
- while (rez==0) {
- rez = BULLET(&AP);
- if (rez) break;
- printf("The previous in-order key is in keyBuffer and its record in yourRecord\n");
- }
- if (rez==EXB_TOP_OF_FILE) rez==0; // expected rez before at beginning of file
- if (rez) return(rez);
-
-
- // Third example performs a GET_EQUAL_OR_GREATER operation, typically
- // used to locate to a key based on a partial search criterion
-
- AP.func = GET_EQUAL_XB;
- AP.handle = indexID;
- AP.recPtr = &yourRecord; // to be filled on return, if found
-
- memset(keyBuffer,0,sizeof(keyBuffer); // clear it out (required)
- strcpy(keyBuffer,"KING"); // find first key starting with 'KING'
- AP.keyPtr = keyBuffer;
-
- rez = BULLET(&AP);
- if (rez==0)
- printf("Matched search key in keyBuffer EXACTLY -- its record is in yourRecord\n");
- else if (rez==EXB_KEY_NOT_FOUND) {
-
- // since not found, get the following in-order one (say, 'KINGSTON')
-
- AP.func = GET_NEXT_XB;
- rez = BULLET(&AP);
- if (rez) return(rez);
-
- printf("Not exact, but next greater key is in keyBuffer and its record is in yourRecord\n");
- }
- else
- return(rez);
-
-
- ΓòÉΓòÉΓòÉ 10.24. Insert Data Record with Key Into Database ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP;
- LOCKPACK LP; // packs used here
-
- LP.func = LOCK_XB;
- LP.handle = indexID; // also locks indexID's owner (its DBF)
- LP.xlMode = LOCK_EXCLUSIVE; // exclusive lock for index
- LP.dlMode = LOCK_EXCLUSIVE; // exclusive lock for data
- LP.nextPtr = NULL; // only one pack
- rez = BULLET(&LP);
- if (rez) return(LP.stat); // rez for transaction-list is NOT the error
-
- AP.func = INSERT_XB;
- AP.handle = indexID;
- AP.recNo = 0; // must be zero
- AP.recPtr = &yourRecord; // contains data record
- AP.keyPtr = keyBuffer; // empty, on return has key stored
- AP.nextPtr = NULL; // only the single pack
- rez = BULLET(&AP);
-
- // on return, as on all transaction-list routines, rez is not the return
- // code but is the pack item that failed (neg if data, pos if index)
-
- if (rez==0)
- printf("okay\n");
- else if (rez < 0)
- printf("insert failed with data, err: %d\n",LP.stat);
- else
- printf("insert failed with index, err: %d\n",LP.stat);
-
-
- // if locked, MUST unlock!
-
- LP.func = UNLOCK_XB;
- LP.handle = indexID;
- LP.nextPtr = NULL;
- rez2 = BULLET(&LP);
- if (rez2) rez2=LP.stat; // rez for transaction-list is NOT the error
- if (rez==0) rez=rez2 // return the most interesting error, if any
-
-
- ΓòÉΓòÉΓòÉ 10.25. Update Data Record with Key Update ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- ACCESSPACK AP;
- LOCKPACK LP; // packs used here
-
- LP.func = LOCK_XB;
- LP.handle = indexID; // also locks indexID's owner (its DBF)
- LP.xlMode = LOCK_SHARED; // shared lock for index
- LP.dlMode = LOCK_SHARED; // shared lock for data
- LP.nextPtr = NULL; // only one pack
- rez = BULLET(&LP);
- if (rez) return(LP.stat); // rez for transaction-list is NOT the error
-
- AP.func = GET_FIRST_XB; // get first key's data record (and its recNo)
- AP.handle = indexID;
- AP.recPtr = &yourRecord;
- AP.keyPtr = keyBuffer;
- rez = BULLET(&AP);
- if (rez==0) {
-
- // assume that we want to change this entry now (as if we weren't sure
- // that we wanted to initially, hence the initial shared lock) --
- // this requires write access so relock to allow write access
-
- LP.func = RELOCK_XB;
- LP.handle = indexID;
- LP.xlMode = LOCK_EXCLUSIVE; // exclusive lock for index
- LP.dlMode = LOCK_EXCLUSIVE; // exclusive lock for data
- LP.nextPtr = NULL;
- rez = BULLET(&LP);
- if (rez) rez=LP.stat // xaction-list routine so use LP.stat
- if (rez==0) {
-
- strcpy(yourRecord.someField,"new field data");
-
- // AP.recNo has been set by Bullet GET_XB call above
-
- AP.func = UPDATE_XB;
- AP.nextPtr = NULL; // all other AP members set above
- rez = BULLET(&AP);
-
- // on return, as on all transaction-list routines, rez is not the return
- // code but is the pack item that failed (neg if data, pos if index)
-
- if (rez==0)
- printf("okay\n");
- else if (rez < 0)
- printf("update failed with data, err: %d\n",LP.stat);
- else
- printf("update failed with index, err: %d\n",LP.stat);
- }
- else
- printf("relock failed, rez: %d err: %d\n",rez,LP.stat);
- }
-
- // if locked, MUST unlock!
-
- LP.func = UNLOCK_XB;
- LP.handle = indexID;
- LP.nextPtr = NULL;
- rez2 = BULLET(&LP);
- if (rez2) rez2=LP.stat; // rez for transaction-list is NOT the error
- if (rez==0) rez=rez2 // return the most interesting error, if any
-
-
- ΓòÉΓòÉΓòÉ 10.26. Remote Drive, File/Device Check ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- REMOTEPACK RP; // packs used here
-
- // check if file in handle (or device handle) is on a 'network' drive
-
- RP.func = CHECK_REMOTE_XB;
- RP.handle = indexID; // check if indexID handle is on a network
- rez = BULLET(&RP);
- if (rez) return(rez);
- if (RP.isRemote)
- printf("handle is on a network drive\n");
-
- // check if drive is a 'network' drive
-
- RP.func = CHECK_REMOTE_XB;
- RP.handle = 0; // set RP.handle to 0 to check drive
- RP.drive = 0; // check if current drive is network drive
- rez = BULLET(&RP); // to check drive C:, set RP.drive=3
- if (rez) return(rez); // D: is RP.drive=4, and so on
- if (RP.isRemote)
- printf("current drive is a network drive\n");
-
-
- ΓòÉΓòÉΓòÉ 10.27. Relock Individual File ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- LOCKPACK LP;
- STATHANDLEPACK SHP;
- STATDATAPACK SDP;
- STATINDEXPACK SIP; // packs used here
-
- // given a handle, determine if it's DBF or index
- // check if currently locked
- // if locked, determine if shared or exclusive lock
- // if exclusive lock, relock to shared
- // if not locked, do nothing
-
- SHP.func = STAT_HANDLE_XB;
- SHP.handle = passedHandle;
- rez = BULLET(&SHP);
- if (SHP.ID==-1)
- puts("Handle is not a Bullet data or index file\n");
- else {
- if (SHP.ID==0) {
-
- // normally, you lock before calling this routine but since
- // SIP.lockCount and SIP.flags are all that is being checked,
- // and since nothing is to be done if the handle is not locked,
- // it's okay in this instance to use this routine without
- // first explicitly locking the handle
-
- SIP.func = STAT_INDEX_XB;
- SIP.handle = passedHandle;
- rez = BULLET(&SIP);
- if (rez) goto ErrorHandler;
-
- // if locked, check if the lock is exclusive
-
- if (SIP.lockCount) { // count of active full locks
-
- if ((SIP.flags & 4)==0) { // bit2=0 means lock is not shared
-
- // currently exclusive, make it shared
-
- LP.func = RELOCK_INDEX_XB;
- LP.handle = passedHandle;
- LP.xlMode = LOCK_SHARED;
- rez = BULLET(&LP);
- if (rez) goto ErrorHandler;
- }
- }
- }
- else {
-
- SDP.func = STAT_DATA_XB;
- SDP.handle = passedHandle;
- rez = BULLET(&SDP);
- if (rez) goto ErrorHandler;
-
- if (SDP.lockCount) { // count of active full locks
-
- if ((SDP.flags & 4)==0) { // bit2=0 means lock is not shared
-
- // currently exclusive, make it shared
-
- LP.func = RELOCK_DATA_XB;
- LP.handle = passedHandle;
- LP.dlMode = LOCK_SHARED;
- LP.startRec = 0; // entire file
- rez = BULLET(&LP);
- if (rez) goto ErrorHandler;
- }
- }
- }
- }
-
-
- ΓòÉΓòÉΓòÉ 10.28. DOS Disk Routines Through Bullet ΓòÉΓòÉΓòÉ
-
- #include "bullet2.h"
-
- DOSFILEPACK DFP; // packs used here
-
- CHAR dataDirname[] = "under";
- CHAR dataFilename[]= "under\\data.dbf";
- CHAR newFilename[] = "under\\newdata.dbf";
-
- // check if pathame can be access for read/write denynone (expected not here)
-
- DFP.func = ACCESS_FILE_DOS;
- DFP.filenamePtr = dataFilename;
- DFP.asMode = 0x42;
- rez = BULLET(&DFP);
- if (rez==0) return(0); // file already exists
- if (rez==5) return(5); // access denied (exists but in use)
- // other errors possible, too, do the Make and Create and go by those results
-
- // make directory for file
-
- DFP.func = MAKE_DIR_DOS;
- DFP.filenamePtr = dataDirname;
- rez = BULLET(&DFP);
- if (rez) rez=0; // can't create subdirectory (already exists?)
-
- // create file
-
- DFP.func = CREATE_FILE_DOS;
- DFP.filenamePtr = dataFilename;
- DFP.attr = 0; // 'normal' attributes
- rez = BULLET(&DFP);
- if (rez) return(rez); // can't create file
-
- // open file
-
- DFP.func = OPEN_FILE_DOS;
- DFP.filenamePtr = dataFilename;
- DFP.asMode = 0x42;
- rez = BULLET(&DFP);
- if (rez) return(rez); // can't open file
-
- // DFP.handle is set by OPEN_FILE_DOS call above
- // pre-allocate file space
-
- DFP.func = EXPAND_FILE_DOS;
- // DFP.handle already set
- // DFP.asMode already set
- DFP.bytes = 128*1024; // set filesize to 128KB
- rez = BULLET(&DFP);
- if (rez) return(rez); // can't do it (close file before return)
-
- CHAR writeStuff[]="Write this string to offset 888";
-
- DFP.func = SEEK_FILE_DOS;
- // DFP.handle already set
- DFP.seekTo = 888;
- DFP.method = 0; // from start of file
- rez = BULLET(&DFP);
- if (rez) return(rez); // can't do it (close...)
-
- // write the string to disk
-
- DFP.func = WRITE_FILE_DOS;
- // DFP.handle already set
- DFP.bytes = strlen(writeStuff);
- DFP.bufferPtr = writeStuff;
- rez = BULLET(&DFP);
- if (rez) return(rez);
-
- // commit to the deep
-
- DFP.func = COMMIT_FILE_DOS;
- // DFP.handle already set
- rez = BULLET(&DFP);
- if (rez) return(rez);
-
- // reposition to where write started so can read what was written
-
- DFP.func = SEEK_FILE_DOS;
- // DFP.handle already set
- DFP.seekTo = 888;
- DFP.method = 0;
- rez = BULLET(&DFP);
- if (rez) return(rez);
-
- CHAR readBuffer[128];
-
- DFP.func = READ_FILE_DOS;
- // DFP.handle already set
- DFP.bytes = strlen(writeStuff); // read it back
- DFP.bufferPtr = readBuffer;
- rez = BULLET(&DFP);
- if (rez) return(rez);
- if (DFP.bytes != strlen(writeStuff))
- printf("read came up short!\n");
-
- DFP.func = CLOSE_FILE_DOS;
- // DFP.handle already set
- rez = BULLET(&DFP);
- if (rez) return(rez);
- DFP.handle = 0; // it's gone (closed, anyway)
-
- // rename the file
-
- DFP.func = RENAME_FILE_DOS;
- DFP.filenamePtr = dataFilename;
- DFP.newFilenamePtr = newFilename;
- rez = BULLET(&DFP);
- if (rez) return(rez);
-
- // and get rid of it (known now as newFilename)
-
- DFP.func = DELETE_FILE_DOS;
- DFP.filenamePtr = newFilename;
- rez = BULLET(&DFP);
- return(rez);
-
- // Why the DosXXX routines when they're standard API? Not all
- // language tools are able to directly call the API, but can
- // call them indirectly through the Bullet DLL.
-
-
- ΓòÉΓòÉΓòÉ 11. Bullet Errors ΓòÉΓòÉΓòÉ
-
- Bullet error codes are numbered so that they do not overlap OS error codes.
- The first general Bullet error number is 8193, but Bullet also returns select
- system error code numbers instead of duplicating system codes (those listed
- below less than 8192). In addition, if the error is reported by the OS (for
- example, attempting to access a locked file), then the OS error in returned by
- Bullet. For a list of OS errors, see OS/2 Dos API Errors.
-
- System Error Codes
-
- 8 EXB_NOT_ENOUGH_MEMORY
- cannot get memory requested
-
- 38 EXB_UNEXPECTED_EOF
- unexpected end-of-file where bytes requested for read exceeded EOF
-
- 39 EXB_DISK_FULL
- disk full on WriteFile
-
- 80 EXB_FILE_EXISTS
- cannot create file since it already exists
-
-
- 8192+ EXB_OR_WITH_FAULTS
- 1=flush failed on handle close
- 2=free memory failed on handle close
- 4=failed memo handle close (data handle only)
-
- During a CLOSE_XB routine, the close process continues
- regardless of errors, and so the errors are accumulated.
- For example, 8193 means the flush failed, and 8195 means
- both the flush and the free failed (8192+1+2=8195).
- If the error occurred in the actual DosClose() API call,
- only that error is returned (it will be an OS error code).
-
-
- 8300 EXB_ILLEGAL_CMD
- function not allowed
-
- 8301 EXB_OLD_DOS
- OS version < MIN_DOS_NEEDED
-
- 8302 EXB_NOT_INITIALIZED
- init not active, must do INIT_XB before using Bullet
-
- 8303 EXB_ALREADY_INITIALIZED
- init already active, must do EXIT_XB first
-
- 8304 EXB_TOO_MANY_HANDLES
- more than 1024 opens requested,
- or more than license permits (100, 250, 1024)
-
- 8305 EXB_SYSTEM_HANDLE
- Bullet won't use or close handles 0-2
-
- 8306 EXB_FILE_NOT_OPEN
- the handle is not a Bullet handle, including the
- handle supplied in OP.xbLink
-
- 8307 EXB_FILE_IS_DIRTY
- tried to reload header but current still dirty;
- flush the file before reloading the header
-
- 8308 EXB_BAD_FILETYPE
- attempted to do a key file operation on non-key file,
- or a data operation on a non-data file
-
- 8309 EXB_TOO_MANY_PACKS
- too many INSERT, UPDATE, REINDEX, LOCK_XB packs (more than 256)
-
- 8310 EXB_NULL_RECPTR
- null record pointer passed to Bullet (.recPtr==NULL)
-
- 8311 EXB_NULL_KEYPTR
- null key pointer passed to Bullet (.keyPtr==NULL)
-
- 8312 EXB_NULL_MEMOPTR
- null memo pointer passed to Bullet (.memoPtr==NULL)
-
- 8313 EXB_EXPIRED
- evaluation time period has expired, reinstall if time remaining
-
- 8314 EXB_BAD_INDEX
- Query/SetSysVars index selection is beyond the last one
-
- 8315 EXB_RO_INDEX
- SetSysVars index item is read-only
-
- 8316 EXB_FILE_BOUNDS
- file size > 4GB, or greater than the SetSysVars value
-
-
- Multi-access Error Codes
-
- 8401 EXB_BAD_LOCK_MODE
- lock mode (LP) not valid, must be 0 or 1
-
- 8402 EXB_NOTHING_TO_RELOCK
- cannot relock without existing full-lock
-
- 8403 ERR_SHARED_LOCK_ON
- unlikely error, write access needed for flush, but lock is shared
-
-
- Index Error Codes
-
- 8501 EXB_KEY_NOT_FOUND
- exact match of key not found
-
- 8502 EXB_KEY_EXISTS
- key exists already and dups not allowed
-
- 8503 EXB_END_OF_FILE
- already at last index order
-
- 8504 EXB_TOP_OF_FILE
- already at first index order
-
- 8505 EXB_EMPTY_FILE
- nothing to do since no keys
-
- 8506 EXB_CANNOT_GET_LAST
- cannot locate last key
-
- 8507 EXB_BAD_INDEX_STACK
- index file is corrupt
-
- 8508 EXB_BAD_INDEX_READ0
- index file is corrupt
-
- 8509 EXB_BAD_INDEX_WRITE0
- index file is corrupt
-
-
- 8521 EXB_OLD_INDEX
- incompatible Bullet index, use ReindexOld subroutine, if available
-
- 8522 EXB_UNKNOWN_INDEX
- not a Bullet index file
-
- 8523 EXB_KEY_TOO_LONG
- keylength > 62 (or 64 if unique), or is 0
-
-
- 8531 EXB_PARSER_NULL
- parser function pointer is NULL
-
- 8532 EXB_BUILDER_NULL
- build key function pointer is NULL
-
- 8533 EXB_BAD_SORT_FUNC
- CIP.sortFunction not valid (not 1-6, or a custom sort-compare)
-
- 8534 EXB_BAD_NODE_SIZE
- CIP.nodeSize is not 512, 1024, or 2048
-
- 8535 EXB_FILENAME_TOO_LONG
- CIP.filenamePtr->pathname greater than file system allows
-
- This error is detected only for the file system installed,
- and does not detect using pathnames greater than 80 on a FAT
- system if HPFS is installed. The OS returns its own error
- in this case, after the fact.
-
-
- 8541 EXB_KEYX_NULL
- key expression is effectively NULL
-
- 8542 EXB_KEYX_TOO_LONG
- CIP.keyExpPtr->expression is greater than 159 bytes
-
- 8543 EXB_KEYX_SYM_TOO_LONG
- fieldname/funcname in expression is longer than 10 chars
-
- 8544 EXB_KEYX_SYM_UNKNOWN
- fieldname/funcname in expression is unknown or misspelled
-
- 8545 EXB_KEYX_TOO_MANY_SYMS
- too many symbols/fields used in expression (16 max)
-
- 8546 EXB_KEYX_BAD_SUBSTR
- invalid SUBSTR() operand in expression
-
- 8547 EXB_KEYX_BAD_SUBSTR_SZ
- SUBSTR() exceeds field's size
-
- 8548 EXB_KEYX_BAD_FORM
- didn't match expected symbol in expression (missing paren, etc.)
-
-
- 8551 EXB_NO_READS_FOR_RUN
- unlikely error, use different reindex buffer size to fix
-
- 8552 EXB_TOO_MANY_RUNS
- unlikely error, too many runs (64K or more runs)
-
- 8553 EXB_TOO_MANY_RUNS_FOR_BUFFER
- unlikely error, too many runs for run buffer
-
- 8554 EXB_TOO_MANY_DUPLICATES
- more than 64K "identical" keys since the last enumerator used
- was 0xFFFF -- if ever you have this error, REINDEX_XB should
- be used to resequence the enumerators
-
-
- 8561 EXB_INSERT_RECNO_BAD
- AP.recNo cannot be > 0 if inserting with INSERT_XB
-
- 8562 EXB_PREV_APPEND_EMPTY
- no previous append for INSERT_XB yet AP.recNo==0x80000000
-
- 8563 EXB_PREV_APPEND_MISMATCH
- previous append's xbLink does not match this
- -- if this pack's AP.recNo=0x80000000 then this pack's AP.handle
- must be the same handle as that of the last pack that added a record
-
- 8564 EXB_INSERT_KBO_FAILED
- could not back out key at INSERT_XB
-
- 8565 EXB_INSERT_DBO_FAILED
- could not back out data records at INSERT_XB
-
-
- 8571 WRN_NOTHING_TO_UPDATE
- all AP.recNo=0 at UPDATE_XB so nothing to do
-
- 8572 EXB_INTERNAL_UPDATE
- internal error UPDATE_XB, not in handle/record# list
-
- 8573 EXB_FAILED_DATA_RESTORE
- could not restore original data record (*)
-
- 8574 EXB_FAILED_KEY_DELETE
- could not remove new key (*)
-
- 8575 EXB_FAILED_KEY_RESTORE
- could not restore original key(*)
-
- (*) original error, which forced a back-out, has been
- replaced by this error -- this error is always returned
- in the first AP.stat (-1 on data, 1 on index)
-
-
- Data Error Codes
-
- 8601 EXB_EXT_XBLINK
- xbLink handle is not an internal DBF, as was specified during the
- index file's creation -- the Bullet routine called requires a Bullet
- DBF data file (instead use index-only access methods like NEXT_KEY_XB).
-
- 8602 EXB_FIELDNAME_TOO_LONG
- fieldname is > 10 characters
-
- 8603 EXB_RECORD_TOO_LONG
- record length is > 64K
-
- 8604 EXB_FIELD_NOT_FOUND
- fieldname not found in descriptor info
-
- 8605 EXB_BAD_FIELD_COUNT
- fields <= 0 or >= MAX_FIELDS; also use of a field number which
- is beyond the last field
-
- 8606 EXB_BAD_HEADER
- bad header (reclen=0, etc.)
-
- 8607 EXB_BUFFER_TOO_SMALL
- buffer too small (pack buffer < record length)
-
- 8608 EXB_INTERNAL_PACK
- internal error in PackRecords
-
- 8609 EXB_BAD_RECNO
- record number=0 or > records in data file header, or
- pack attempt on empty data file
-
- 8610 WRN_RECORD_TAGGED
- record's tag field matches skip tag
-
-
- Memo Error Codes
-
- 8701 WRN_CANNOT_OPEN_MEMO
- the DBF header has bits 3 & 7 set, which indicates that a memo
- file is attached to this DBF, but the DBT memo file failed to open
- -- the DBF open continues, with this warning code returned
-
- 8702 EXB_MEMO_NOT_OPEN
- no open memo file for operation
-
- 8703 EXB_BAD_BLOCKSIZE
- memo blocksize must be at least 24 bytes
-
- 8704 EXB_MEMO_DELETED
- memo is deleted
-
- 8705 EXB_MEMO_PAST_END
- memo data requested is past end of record
-
- 8706 EXB_BAD_MEMONO
- memo number is not valid
-
- 8707 EXB_MEMO_IN_USE
- memo add encountered likely corrupt memo file
- -- avail list indicates this memo record is deleted, but the memoAvail
- link for the memo indicates it is use (memoAvail link==0x8FFFF)
-
- 8708 EXB_BAD_AVAIL_LINK
- memo avail link cannot be valid (e.g., memoAvail==0)
-
- 8709 EXB_MEMO_ZERO_SIZE
- memo data has no size (size is 0)
-
- 8710 EXB_MEMO_IS_SMALLER
- memo attempt to shrink but memo size is already <= size requested
-
-
- ΓòÉΓòÉΓòÉ 12. OS/2 Dos API Errors ΓòÉΓòÉΓòÉ
-
-
- 0 NO_ERROR
- No error occurred.
-
- 1 ERROR_INVALID_FUNCTION
- Invalid function number.
-
- 2 ERROR_FILE_NOT_FOUND
- File not found.
-
- 3 ERROR_PATH_NOT_FOUND
- Path not found.
-
- 4 ERROR_TOO_MANY_OPEN_FILES
- Too many open files (no handles left).
-
- 5 ERROR_ACCESS_DENIED
- Access denied.
-
- 6 ERROR_INVALID_HANDLE
- Invalid handle.
-
- 7 ERROR_ARENA_TRASHED
- Memory control blocks destroyed.
-
- 8 ERROR_NOT_ENOUGH_MEMORY
- Insufficient memory.
-
- 9 ERROR_INVALID_BLOCK
- Invalid memory-block address.
-
- 10 ERROR_BAD_ENVIRONMENT
- Invalid environment.
-
- 11 ERROR_BAD_FORMAT
- Invalid format.
-
- 12 ERROR_INVALID_ACCESS
- Invalid access code.
-
- 13 ERROR_INVALID_DATA
- Invalid data.
-
- 14 Reserved.
-
-
- 15 ERROR_INVALID_DRIVE
- Invalid drive specified.
-
- 16 ERROR_CURRENT_DIRECTORY
- Attempting to remove current directory.
-
- 17 ERROR_NOT_SAME_DEVICE
- Not same device.
-
- 18 ERROR_NO_MORE_FILES
- No more files.
-
- 19 ERROR_WRITE_PROTECT
- Attempt to write on write-protected diskette.
-
- 20 ERROR_BAD_UNIT
- Unknown unit.
-
- 21 ERROR_NOT_READY
- Drive not ready.
-
- 22 ERROR_BAD_COMMAND
- Unknown command.
-
- 23 ERROR_CRC
- Data error - cyclic redundancy check.
-
- 24 ERROR_BAD_LENGTH
- Invalid request structure length.
-
- 25 ERROR_SEEK
- Seek error.
-
- 26 ERROR_NOT_DOS_DISK
- Unknown media type.
-
- 27 ERROR_SECTOR_NOT_FOUND
- Sector not found.
-
- 28 ERROR_OUT_OF_PAPER
- Printer is out of paper.
-
- 29 ERROR_WRITE FAULT
- Write fault.
-
- 30 ERROR_READ_FAULT
- Read fault.
-
- 31 ERROR_GEN_FAILURE
- General failure.
-
- 32 ERROR_SHARING_VIOLATION
- Sharing violation.
-
- 33 ERROR_LOCK_VIOLATION
- Lock violation.
-
- 34 ERROR_WRONG_DISK
- Invalid disk change.
-
- 35 ERROR_FCB_UNAVAILABLE
- FCB unavailable.
-
- 36 ERROR_SHARING_BUFFER_EXCEEDED
- Sharing buffer overflow.
-
- 37 ERROR_CODE_PAGE_MISMATCHED
- Code page does not match.
-
- 38 ERROR_HANDLE_EOF
- End of file reached.
-
- 39 ERROR_HANDLE_DISK_FULL
- Disk is full.
-
- 40-49 Reserved.
-
-
- 50 ERROR_NOT_SUPPORTED
- Network request not supported.
-
- 51 ERROR_REM_NOT_LIST
- Remote network node is not online.
-
- 52 ERROR_DUP_NAME
- Duplicate file name in network.
-
- 53 ERROR_BAD_NETPATH
- Network path not found.
-
- 54 ERROR_NETWORK_BUSY
- Network is busy.
-
- 55 ERROR_DEV_NOT_EXIST
- Device is not installed in network.
-
- 56 ERROR_TOO_MANY_CMDS
- Network command limit reached.
-
- 57 ERROR_ADAP_HDW_ERR
- Network adapter hardware error.
-
- 58 ERROR_BAD_NET_RESP
- Incorrect response in network.
-
- 59 ERROR_UNEXP_NET_ERR
- Unexpected error in network.
-
- 60 ERROR_BAD_REM_ADAP
- Remote network adapter error.
-
- 61 ERROR_PRINTQ_FULL
- Network printer queue is full.
-
- 62 ERROR_NO_SPOOL_SPACE
- No space in print spool file.
-
- 63 ERROR_PRINT_CANCELLED
- Print spool file deleted.
-
- 64 ERROR_NETNAME_DELETED
- Network name deleted.
-
- 65 ERROR_NETWORK_ACCESS_DENIED
- Access to network denied.
-
- 66 ERROR_BAD_DEV_TYPE
- Device type invalid for network.
-
- 67 ERROR_BAD_NET_NAME
- Network name not found.
-
- 68 ERROR_TOO_MANY_NAMES
- Network name limit exceeded.
-
- 69 ERROR_TOO_MANY_SESS
- Network session limit exceeded.
-
- 70 ERROR_SHARING_PAUSED
- Temporary pause in network.
-
- 71 ERROR_REQ_NOT_ACCEP
- Network request denied.
-
- 72 ERROR_REDIR_PAUSED
- Pause in network print disk redirection.
-
- 73 ERROR_SBCS_ATT_WRITE_PROT
- Attempted write on protected disk.
-
- 74 ERROR_SBCS_GENERAL_FAILURE
- General failure, single-byte character set.
-
- 75-79 Reserved.
-
-
- 80 ERROR_FILE_EXISTS
- File exists.
-
- 81 ERROR_DUP_FCB
- Reserved.
-
- 82 ERROR_CANNOT_MAKE
- Cannot make directory entry.
-
- 83 ERROR_FAIL_I24
- Failure on INT 24.
-
- 84 ERROR_OUT_OF_STRUCTURES
- Too many redirections.
-
- 85 ERROR_ALREADY_ASSIGNED
- Duplicate redirection.
-
- 86 ERROR_INVALID_PASSWORD
- Invalid password.
-
- 87 ERROR_INVALID_PARAMETER
- Invalid parameter.
-
- 88 ERROR_NET_WRITE_FAULT
- Network device fault.
-
- 89 ERROR_NO_PROC_SLOTS
- No process slots available.
-
- 90 ERROR_NOT_FROZEN
- System error.
-
- 91 ERR_TSTOVFL
- Timer service table overflow.
-
- 92 ERR_TSTDUP
- Timer service table duplicate.
-
- 93 ERROR_NO_ITEMS
- No items to work on.
-
- 95 ERROR_INTERRUPT
- Interrupted system call.
-
- 99 ERROR_DEVICE_IN_USE
- Device in use.
-
- 100 ERROR_TOO_MANY_SEMAPHORES
- User/system open semaphore limit reached.
-
- 101 ERROR_EXCL_SEM_ALREADY_OWNED
- Exclusive semaphore already owned.
-
- 102 ERROR_SEM_IS_SET
- DosCloseSem found semaphore set.
-
- 103 ERROR_TOO_MANY_SEM_REQUESTS
- Too many exclusive semaphore requests.
-
- 104 ERROR_INVALID_AT_INTERRUPT_TIME
- Operation invalid at interrupt time.
-
- 105 ERROR_SEM_OWNER_DIED
- Previous semaphore owner terminated without freeing semaphore.
-
- 106 ERROR_SEM_USER_LIMIT
- Semaphore limit exceeded.
-
- 107 ERROR_DISK_CHANGE
- Insert drive B disk into drive A.
-
- 108 ERROR_DRIVE_LOCKED
- Drive locked by another process.
-
- 109 ERROR_BROKEN_PIPE
- Write on pipe with no reader.
-
- 110 ERROR_OPEN_FAILED
- Open/create failed due to explicit fail command.
-
- 111 ERROR_BUFFER_OVERFLOW
- Buffer passed to system call too small to hold return data.
-
- 112 ERROR_DISK_FULL
- Not enough space on the disk.
-
- 113 ERROR_NO_MORE_SEARCH_HANDLES
- Cannot allocate another search structure and handle.
-
- 114 ERROR_INVALID_TARGET_HANDLE
- Target handle in DosDupHandle invalid.
-
- 115 ERROR_PROTECTION_VIOLATION
- Invalid user virtual address.
-
- 116 ERROR_VIOKBD_REQUEST
- Error on display write or keyboard read.
-
- 117 ERROR_INVALID_CATEGORY
- Category for DevIOCtl not defined.
-
- 118 ERROR_INVALID_VERIFY_SWITCH
- Invalid value passed for verify flag.
-
- 119 ERROR_BAD_DRIVER_LEVEL
- Level four driver not found.
-
- 120 ERROR_CALL_NOT_IMPLEMENTED
- Invalid function called.
-
- 121 ERROR_SEM_TIMEOUT
- Time-out occurred from semaphore API function.
-
- 122 ERROR_INSUFFICIENT_BUFFER
- Data buffer too small.
-
- 123 ERROR_INVALID_NAME
- Illegal character or invalid file-system name.
-
- 124 ERROR_INVALID_LEVEL
- Non-implemented level for information retrieval or setting.
-
- 125 ERROR_NO_VOLUME_LABEL
- No volume label found with DosQueryFSInfo function.
-
- 126 ERROR_MOD_NOT_FOUND
- Module handle not found with DosQueryProcAddr(),
- DosQueryModAddr().
-
- 127 ERROR_PROC_NOT_FOUND
- Procedure address not found with DosQueryProcAddr().
-
- 128 ERROR_WAIT_NO_CHILDREN
- DosWaitChild finds no children.
-
- 129 ERROR_CHILD_NOT_COMPLETE
- DosWaitChild children not terminated.
-
- 130 ERROR_DIRECT_ACCESS_HANDLE
- Handle operation invalid for direct disk-access handles.
-
- 131 ERROR_NEGATIVE_SEEK
- Attempting seek to negative offset.
-
- 132 ERROR_SEEK_ON_DEVICE
- Application trying to seek on device or pipe.
-
- 133 ERROR_IS_JOIN_TARGET
- Drive has previously joined drives.
-
- 134 ERROR_IS_JOINED
- Drive is already joined.
-
- 135 ERROR_IS_SUBSTED
- Drive is already substituted.
-
- 136 ERROR_NOT_JOINED
- Cannot delete drive that is not joined.
-
- 137 ERROR_NOT_SUBSTED
- Cannot delete drive that is not substituted.
-
- 138 ERROR_JOIN_TO_JOIN
- Cannot join to a joined drive.
-
- 139 ERROR_SUBST_TO_SUBST
- Cannot substitute to a substituted drive.
-
- 140 ERROR_JOIN_TO_SUBST
- Cannot join to a substituted drive.
-
- 141 ERROR_SUBST_TO_JOIN
- Cannot substitute to a joined drive.
-
- 142 ERROR_BUSY_DRIVE
- Specified drive is busy.
-
- 143 ERROR_SAME_DRIVE
- Cannot join or substitute a drive to a directory on the same drive.
-
- 144 ERROR_DIR_NOT_ROOT
- Directory must be a subdirectory of the root.
-
- 145 ERROR_DIR_NOT_EMPTY
- Directory must be empty to use join command.
-
- 146 ERROR_IS_SUBST_PATH
- Path specified is being used in a substitute.
-
- 147 ERROR_IS_JOIN_PATH
- Path specified is being used in a join.
-
- 148 ERROR_PATH_BUSY
- Path specified is being used by another process.
-
- 149 ERROR_IS_SUBST_TARGET
- Cannot join or substitute a drive that has a directory that is the
- target of a previous substitute.
-
- 150 ERROR_SYSTEM_TRACE
- System trace error.
-
- 151 ERROR_INVALID_EVENT_COUNT
- DosWaitMuxWaitSem errors.
-
- 152 ERROR_TOO_MANY_MUXWAITERS
- System limit of 100 entries reached.
-
- 153 ERROR_INVALID_LIST_FORMAT
- Invalid list format.
-
- 154 ERROR_LABEL_TOO_LONG
- Volume label too big.
-
- 155 ERROR_TOO_MANY_TCBS
- Cannot create another TCB.
-
- 156 ERROR_SIGNAL_REFUSED
- Signal refused.
-
- 157 ERROR_DISCARDED
- Segment is discarded.
-
- 158 ERROR_NOT_LOCKED
- Segment is not locked.
-
- 159 ERROR_BAD_THREADID_ADDR
- Invalid thread-identity address.
-
- 160 ERROR_BAD_ARGUMENTS
- Invalid environment pointer.
-
- 161 ERROR_BAD_PATHNAME
- Invalid path name passed to exec.
-
- 162 ERROR_SIGNAL_PENDING
- Signal already pending.
-
- 163 ERROR_UNCERTAIN_MEDIA
- Error with INT 24 mapping.
-
- 164 ERROR_MAX_THRDS_REACHED
- No more process slots.
-
- 165 ERROR_MONITORS_NOT_SUPPORTED
- Error with INT 24 mapping.
-
- 166 ERROR_UNC_DRIVER_NOT_INSTALLED
- Default redirection return code.
-
- 167 ERROR_LOCK_FAILED
- Locking failed.
-
- 168 ERROR_SWAPIO_FAILED
- Swap I/O failed.
-
- 169 ERROR_SWAPIN_FAILED
- Swap in failed.
-
- 170 ERROR_BUSY
- Segment is busy.
-
- 171-172 Reserved.
-
-
- 173 ERROR_CANCEL_VIOLATION
- A lock request is not outstanding for the specified file range, or the
- range length is zero.
-
- 174 ERROR_ATOMIC_LOCK_NOT_SUPPORTED
- The file-system driver (FSD) does not support atomic lock operations.
- Versions of OS/2 prior to version 2.00 do not support atomic lock
- operations.
-
- 175 ERROR_READ_LOCKS_NOT_SUPPORTED
- The file system driver (FSD) does not support shared read locks.
-
- 176-179 Reserved.
-
-
- 180 ERROR_INVALID_SEGMENT_NUMBER
- Invalid segment number.
-
- 181 ERROR_INVALID_CALLGATE
- Invalid call gate.
-
- 182 ERROR_INVALID_ORDINAL
- Invalid ordinal.
-
- 183 ERROR_ALREADY_EXISTS
- Shared segment already exists.
-
- 184 ERROR_NO_CHILD_PROCESS
- No child process to wait for.
-
- 185 ERROR_CHILD_ALIVE_NOWAIT
- NoWait specified and child alive.
-
- 186 ERROR_INVALID_FLAG_NUMBER
- Invalid flag number.
-
- 187 ERROR_SEM_NOT_FOUND
- Semaphore does not exist.
-
- 188 ERROR_INVALID_STARTING_CODESEG
- Invalid starting code segment, incorrect END (label) directive.
-
- 189 ERROR_INVALID_STACKSEG
- Invalid stack segment.
-
- 190 ERROR_INVALID_MODULETYPE
- Invalid module type - dynamic-link library file cannot be used as an
- application. Application cannot be used as a dynamic-link library.
-
- 191 ERROR_INVALID_EXE_SIGNATURE
- Invalid EXE signature - file is a DOS mode program or an improper
- program.
-
- 192 ERROR_EXE_MARKED_INVALID
- EXE marked invalid - link detected errors when the application was
- created.
-
- 193 ERROR_BAD_EXE_FORMAT
- Invalid EXE format - file is a DOS mode program or an improper
- program.
-
- 194 ERROR_ITERATED_DATA_EXCEEDS_64k
- Iterated data exceeds 64KB - there is more than 64KB of data in one
- of the segments of the file.
-
- 195 ERROR_INVALID_MINALLOCSIZE
- Invalid minimum allocation size - the size is specified to be less than
- the size of the segment data in the file.
-
- 196 ERROR_DYNLINK_FROM_INVALID_RING
- Dynamic link from invalid privilege level - privilege level 2 routine
- cannot link to dynamic-link libraries.
-
- 197 ERROR_IOPL_NOT_ENABLED
- IOPL not enabled - IOPL set to NO in CONFIG.SYS.
-
- 198 ERROR_INVALID_SEGDPL
- Invalid segment descriptor privilege level - can only have privilege
- levels of 2 and 3.
-
- 199 ERROR_AUTODATASEG_EXCEEDS_64k
- Automatic data segment exceeds 64KB.
-
- 200 ERROR_RING2SEG_MUST_BE_MOVABLE
- Privilege level 2 segment must be movable.
-
- 201 ERROR_RELOC_CHAIN_XEEDS_SEGLIM
- Relocation chain exceeds segment limit.
-
- 202 ERROR_INFLOOP_IN_RELOC_CHAIN
- Infinite loop in relocation chain segment.
-
- 203 ERROR_ENVVAR_NOT_FOUND
- Environment variable not found.
-
- 204 ERROR_NOT_CURRENT_CTRY
- Not current country.
-
- 205 ERROR_NO_SIGNAL_SENT
- No signal sent - no process in the command subtree has a signal
- handler.
-
- 206 ERROR_FILENAME_EXCED_RANGE
- File name or extension is greater than 8.3 characters.
-
- 207 ERROR_RING2_STACK_IN_USE
- Privilege level 2 stack is in use.
-
- 208 ERROR_META_EXPANSION_TOO_LONG
- Meta (global) expansion is too long.
-
- 209 ERROR_INVALID_SIGNAL_NUMBER
- Invalid signal number.
-
- 210 ERROR_THREAD_1_INACTIVE
- Inactive thread.
-
- 211 ERROR_INFO_NOT_AVAIL
- File system information is not available for this file.
-
- 212 ERROR_LOCKED
- Locked error.
-
- 213 ERROR_BAD_DYNALINK
- Attempted to execute a non-family API in DOS mode.
-
- 214 ERROR_TOO_MANY_MODULES
- Too many modules.
-
- 215 ERROR_NESTING_NOT_ALLOWED
- Nesting is not allowed.
-
- 217 ERROR_ZOMBIE_PROCESS
- Zombie process.
-
- 218 ERROR_STACK_IN_HIGH_MEMORY
- Stack is in high memory.
-
- 219 ERROR_INVALID_EXITROUTINE_RING
- Invalid exit routine ring.
-
- 220 ERROR_GETBUF_FAILED
- Get buffer failed.
-
- 221 ERROR_FLUSHBUF_FAILED
- Flush buffer failed.
-
- 222 ERROR_TRANSFER_TOO_LONG
- Transfer is too long.
-
- 224 ERROR_SMG_NO_TARGET_WINDOW
- The application window was created without the FCF_TASKLIST
- style, or the application window not yet been created or has already
- been destroyed.
-
- 228 ERROR_NO_CHILDREN
- No child process.
-
- 229 ERROR_INVALID_SCREEN_GROUP
- Invalid session.
-
- 230 ERROR_BAD_PIPE
- Non-existent pipe or invalid operation.
-
- 231 ERROR_PIPE_BUSY
- Pipe is busy.
-
- 232 ERROR_NO_DATA
- No data available on non-blocking read.
-
- 233 ERROR_PIPE_NOT_CONNECTED
- Pipe was disconnected by server.
-
- 234 ERROR_MORE_DATA
- More data is available.
-
- 240 ERROR_VC_DISCONNECTED
- Session was dropped due to errors.
-
- 250 ERROR_CIRCULARITY_REQUESTED
- Renaming a directory that would cause a circularity problem.
-
- 251 ERROR_DIRECTORY_IN_CDS
- Renaming a directory that is in use.
-
- 252 ERROR_INVALID_FSD_NAME
- Trying to access nonexistent FSD.
-
- 253 ERROR_INVALID_PATH
- Invalid pseudo device.
-
- 254 ERROR_INVALID_EA_NAME
- Invalid character in name, or invalid cbName.
-
- 255 ERROR_EA_LIST_INCONSISTENT
- List does not match its size, or there are invalid EAs in the list.
-
- 256 ERROR_EA_LIST_TOO_LONG
- FEAList is longer than 64K-1 bytes.
-
- 257 ERROR_NO_META_MATCH
- String does not match expression.
-
- 259 ERROR_NO_MORE_ITEMS
- DosQueryFSAttach ordinal query.
-
- 260 ERROR_SEARCH_STRUC_REUSED
- DOS mode findfirst/next search structure reused.
-
- 261 ERROR_CHAR_NOT_FOUND
- Character not found.
-
- 262 ERROR_TOO_MUCH_STACK
- Stack request exceeds system limit.
-
- 263 ERROR_INVALID_ATTR
- Invalid attribute.
-
- 264 ERROR_INVALID_STARTING_RING
- Invalid starting ring.
-
- 265 ERROR_INVALID_DLL_INIT_RING
- Invalid DLL INIT ring.
-
- 266 ERROR_CANNOT_COPY
- Cannot copy.
-
- 267 ERROR_DIRECTORY
- Used by DOSCOPY in doscall1.
-
- 268 ERROR_OPLOCKED_FILE
- Oplocked file.
-
- 269 ERROR_OPLOCK_THREAD_EXISTS
- Oplock thread exists.
-
- 270 ERROR_VOLUME_CHANGED
- Volume changed.
-
- 271-273 Reserved.
-
-
- 274 ERROR_ALREADY_SHUTDOWN
- System is already shut down.
-
- 275 ERROR_EAS_DIDNT_FIT
- Buffer is not big enough to hold the EAs.
-
- 276 ERROR_EA_FILE_CORRUPT
- EA file has been damaged.
-
- 277 ERROR_EA_TABLE_FULL
- EA table is full.
-
- 278 ERROR_INVALID_EA_HANDLE
- EA handle is invalid.
-
- 279 ERROR_NO_CLUSTER
- No cluster.
-
- 280 ERROR_CREATE_EA_FILE
- Cannot create the EA file.
-
- 281 ERROR_CANNOT_OPEN_EA_FILE
- Cannot open the EA file.
-
- 282 ERROR_EAS_NOT_SUPPORTED
- Destination file system does not support EAs.
-
- 283 ERROR_NEED_EAS_FOUND
- Destination file system does not support EAs, and the source file's
- EAs contain a need EA.
-
- 284 ERROR_DUPLICATE_HANDLE
- The handle already exists.
-
- 285 ERROR_DUPLICATE_NAME
- The name already exists.
-
- 286 ERROR_EMPTY_MUXWAIT
- The list of semaphores in a muxwait semaphore is empty.
-
- 287 ERROR_MUTEX_OWNED
- The calling thread owns one or more of the mutex semaphores in the
- list.
-
- 288 ERROR_NOT_OWNER
- Caller does not own the semaphore.
-
- 289 ERROR_PARAM_TOO_SMALL
- Parameter is not large enough to contain all of the semaphore
- records in the muxwait semaphore.
-
- 290 ERROR_TOO_MANY_HANDLES
- Limit reached for number of handles.
-
- 291 ERROR_TOO_MANY_OPENS
- There are too many files or semaphores open.
-
- 292 ERROR_WRONG_TYPE
- Attempted to create wrong type of semaphore.
-
- 293 ERROR_UNUSED_CODE
- Code is not used.
-
- 294 ERROR_THREAD_NOT_TERMINATED
- Thread has not terminated.
-
- 295 ERROR_INIT_ROUTINE_FAILED
- Initialization routine failed.
-
- 296 ERROR_MODULE_IN_USE
- Module is in use.
-
- 297 ERROR_NOT_ENOUGH_WATCHPOINTS
- There are not enough watchpoints.
-
- 298 ERROR_TOO_MANY_POSTS
- Post count limit was reached for an event semaphore.
-
- 299 ERROR_ALREADY_POSTED
- Event semaphore is already posted.
-
- 300 ERROR_ALREADY_RESET
- Event semaphore is already reset.
-
- 301 ERROR_SEM_BUSY
- Semaphore is busy.
-
- 302 Reserved
-
-
- 303 ERROR_INVALID_PROCID
- Invalid process identity.
-
- 304 ERROR_INVALID_PDELTA
- Invalid priority delta.
-
- 305 ERROR_NOT_DESCENDANT
- Not descendant.
-
- 306 ERROR_NOT_SESSION_MANAGER
- Requestor not session manager.
-
- 307 ERROR_INVALID_PCLASS
- Invalid P class.
-
- 308 ERROR_INVALID_SCOPE
- Invalid scope.
-
- 309 ERROR_INVALID_THREADID
- Invalid thread identity.
-
- 310 ERROR_DOSSUB_SHRINK
- Cannot shrink segment - DosSubSetMem.
-
- 311 ERROR_DOSSUB_NOMEM
- No memory to satisfy request - DosSubAllocMem.
-
- 312 ERROR_DOSSUB_OVERLAP
- Overlap of the specified block with a block of allocated memory -
- DosSubFreeMem.
-
- 313 ERROR_DOSSUB_BADSIZE
- Invalid size parameter - DosSubAllocMem or DosSubFreeMem.
-
- 314 ERROR_DOSSUB_BADFLAG
- Invalid flag parameter - DosSubSetMem.
-
- 315 ERROR_DOSSUB_BADSELECTOR
- Invalid segment selector.
-
- 316 ERROR_MR_MSG_TOO_LONG
- Message too long for buffer.
-
- 317 ERROR_MR_MID_NOT_FOUND
- Message identity number not found.
-
- 318 ERROR_MR_UN_ACC_MSGF
- Unable to access message file.
-
- 319 ERROR_MR_INV_MSGF_FORMAT
- Invalid message file format.
-
- 320 ERROR_MR_INV_IVCOUNT
- Invalid insertion variable count.
-
- 321 ERROR_MR_UN_PERFORM
- Unable to perform function.
-
- 322 ERROR_TS_WAKEUP
- Unable to wake up.
-
- 323 ERROR_TS_SEMHANDLE
- Invalid system semaphore.
-
- 324 ERROR_TS_NOTIMER
- No timers available.
-
- 326 ERROR_TS_HANDLE
- Invalid timer handle.
-
- 327 ERROR_TS_DATETIME
- Date or time invalid.
-
- 328 ERROR_SYS_INTERNAL
- Internal system error.
-
- 329 ERROR_QUE_CURRENT_NAME
- Current queue name does not exist.
-
- 330 ERROR_QUE_PROC_NOT_OWNED
- Current process does not own queue.
-
- 331 ERROR_QUE_PROC_OWNED
- Current process owns queue.
-
- 332 ERROR_QUE_DUPLICATE
- Duplicate queue name.
-
- 333 ERROR_QUE_ELEMENT_NOT_EXIST
- Queue element does not exist.
-
- 334 ERROR_QUE_NO_MEMORY
- Inadequate queue memory.
-
- 335 ERROR_QUE_INVALID_NAME
- Invalid queue name.
-
- 336 ERROR_QUE_INVALID_PRIORITY
- Invalid queue priority parameter.
-
- 337 ERROR_QUE_INVALID_HANDLE
- Invalid queue handle.
-
- 338 ERROR_QUE_LINK_NOT_FOUND
- Queue link not found.
-
- 339 ERROR_QUE_MEMORY_ERROR
- Queue memory error.
-
- 340 ERROR_QUE_PREV_AT_END
- Previous queue element was at end of queue.
-
- 341 ERROR_QUE_PROC_NO_ACCESS
- Process does not have access to queues.
-
- 342 ERROR_QUE_EMPTY
- Queue is empty.
-
- 343 ERROR_QUE_NAME_NOT_EXIST
- Queue name does not exist.
-
- 344 ERROR_QUE_NOT_INITIALIZED
- Queues not initialized.
-
- 345 ERROR_QUE_UNABLE_TO_ACCESS
- Unable to access queues.
-
- 346 ERROR_QUE_UNABLE_TO_ADD
- Unable to add new queue.
-
- 347 ERROR_QUE_UNABLE_TO_INIT
- Unable to initialize queues.
-
- 349 ERROR_VIO_INVALID_MASK
- Invalid function replaced.
-
- 350 ERROR_VIO_PTR
- Invalid pointer to parameter.
-
- 351 ERROR_VIO_APTR
- Invalid pointer to attribute.
-
- 352 ERROR_VIO_RPTR
- Invalid pointer to row.
-
- 353 ERROR_VIO_CPTR
- Invalid pointer to column.
-
- 354 ERROR_VIO_LPTR
- Invalid pointer to length.
-
- 355 ERROR_VIO_MODE
- Unsupported screen mode.
-
- 356 ERROR_VIO_WIDTH
- Invalid cursor width value.
-
- 357 ERROR_VIO_ATTR
- Invalid cursor attribute value.
-
- 358 ERROR_VIO_ROW
- Invalid row value.
-
- 359 ERROR_VIO_COL
- Invalid column value.
-
- 360 ERROR_VIO_TOPROW
- Invalid TopRow value.
-
- 361 ERROR_VIO_BOTROW
- Invalid BotRow value.
-
- 362 ERROR_VIO_RIGHTCOL
- Invalid right column value.
-
- 363 ERROR_VIO_LEFTCOL
- Invalid left column value.
-
- 364 ERROR_SCS_CALL
- Call issued by other than session manager.
-
- 365 ERROR_SCS_VALUE
- Value is not for save or restore.
-
- 366 ERROR_VIO_WAIT_FLAG
- Invalid wait flag setting.
-
- 367 ERROR_VIO_UNLOCK
- Screen not previously locked.
-
- 368 ERROR_SGS_NOT_SESSION_MGR
- Caller not session manager.
-
- 369 ERROR_SMG_INVALID_SGID
- Invalid session identity.
-
- 369 ERROR_SMG_INVALID_SESSION_ID
- Invalid session ID.
-
- 370 ERROR_SMG_NOSG
- No sessions available.
-
- 370 ERROR_SMG_NO_SESSIONS
- No sessions available.
-
- 371 ERROR_SMG_GRP_NOT_FOUND
- Session not found.
-
- 371 ERROR_SMG_SESSION_NOT_FOUND
- Session not found.
-
- 372 ERROR_SMG_SET_TITLE
- Title sent by shell or parent cannot be changed.
-
- 373 ERROR_KBD_PARAMETER
- Invalid parameter to keyboard.
-
- 374 ERROR_KBD_NO_DEVICE
- No device.
-
- 375 ERROR_KBD_INVALID_IOWAIT
- Invalid I/O wait specified.
-
- 376 ERROR_KBD_INVALID_LENGTH
- Invalid length for keyboard.
-
- 377 ERROR_KBD_INVALID_ECHO_MASK
- Invalid echo mode mask.
-
- 378 ERROR_KBD_INVALID_INPUT_MASK
- Invalid input mode mask.
-
- 379 ERROR_MON_INVALID_PARMS
- Invalid parameters to DosMon.
-
- 380 ERROR_MON_INVALID_DEVNAME
- Invalid device name string.
-
- 381 ERROR_MON_INVALID_HANDLE
- Invalid device handle.
-
- 382 ERROR_MON_BUFFER_TOO_SMALL
- Buffer too small.
-
- 383 ERROR_MON_BUFFER_EMPTY
- Buffer is empty.
-
- 384 ERROR_MON_DATA_TOO_LARGE
- Data record is too large.
-
- 385 ERROR_MOUSE_NO_DEVICE
- Mouse device closed (invalid device handle).
-
- 386 ERROR_MOUSE_INV_HANDLE
- Mouse device closed (invalid device handle).
-
- 387 ERROR_MOUSE_INV_PARMS
- Parameters invalid for display mode.
-
- 388 ERROR_MOUSE_CANT_RESET
- Function assigned and cannot be reset.
-
- 389 ERROR_MOUSE_DISPLAY_PARMS
- Parameters invalid for display mode.
-
- 390 ERROR_MOUSE_INV_MODULE
- Module not valid.
-
- 391 ERROR_MOUSE_INV_ENTRY_PT
- Entry point not valid.
-
- 392 ERROR_MOUSE_INV_MASK
- Function mask invalid.
-
- 393 NO_ERROR_MOUSE_NO_DATA
- No valid data.
-
- 394 NO_ERROR_MOUSE_PTR_DRAWN
- Pointer drawn.
-
- 395 ERROR_INVALID_FREQUENCY
- Invalid frequency for beep.
-
- 396 ERROR_NLS_NO_COUNTRY_FILE
- Cannot find COUNTRY.SYS file.
-
- 397 ERROR_NLS_OPEN_FAILED
- Cannot open COUNTRY.SYS file.
-
- 398 ERROR_NLS_NO_CTRY_CODE
- Country code not found.
-
- 398 ERROR_NO_COUNTRY_OR_CODEPAGE
- Country code not found.
-
- 399 ERROR_NLS_TABLE_TRUNCATED
- Table returned information truncated, buffer is too small.
-
- 400 ERROR_NLS_BAD_TYPE
- Selected type does not exist.
-
- 401 ERROR_NLS_TYPE_NOT_FOUND
- Selected type is not in file.
-
- 402 ERROR_VIO_SMG_ONLY
- Valid from session manager only.
-
- 403 ERROR_VIO_INVALID_ASCIIZ
- Invalid ASCIIZ length.
-
- 404 ERROR_VIO_DEREGISTER
- VioDeRegister not allowed.
-
- 405 ERROR_VIO_NO_POPUP
- Pop-up window not allocated.
-
- 406 ERROR_VIO_EXISTING_POPUP
- Pop-up window on screen (NoWait).
-
- 407 ERROR_KBD_SMG_ONLY
- Valid from session manager only.
-
- 408 ERROR_KBD_INVALID_ASCIIZ
- Invalid ASCIIZ length.
-
- 409 ERROR_KBD_INVALID_MASK
- Invalid replacement mask.
-
- 410 ERROR_KBD_REGISTER
- KbdRegister not allowed.
-
- 411 ERROR_KBD_DEREGISTER
- KbdDeRegister not allowed.
-
- 412 ERROR_MOUSE_SMG_ONLY
- Valid from session manager only.
-
- 413 ERROR_MOUSE_INVALID_ASCIIZ
- Invalid ASCIIZ length.
-
- 414 ERROR_MOUSE_INVALID_MASK
- Invalid replacement mask.
-
- 415 ERROR_MOUSE_REGISTER
- Mouse register not allowed.
-
- 416 ERROR_MOUSE_DEREGISTER
- Mouse deregister not allowed.
-
- 417 ERROR_SMG_BAD_ACTION
- Invalid action specified.
-
- 418 ERROR_SMG_INVALID_CALL
- INIT called more than once, or invalid session identity.
-
- 419 ERROR_SCS_SG_NOTFOUND
- New session number.
-
- 420 ERROR_SCS_NOT_SHELL
- Caller is not shell.
-
- 421 ERROR_VIO_INVALID_PARMS
- Invalid parameters passed.
-
- 422 ERROR_VIO_FUNCTION_OWNED
- Save/restore already owned.
-
- 423 ERROR_VIO_RETURN
- Non-destruct return (undo).
-
- 424 ERROR_SCS_INVALID_FUNCTION
- Caller invalid function.
-
- 425 ERROR_SCS_NOT_SESSION_MGR
- Caller not session manager.
-
- 426 ERROR_VIO_REGISTER
- Vio register not allowed.
-
- 427 ERROR_VIO_NO_MODE_THREAD
- No mode restore thread in SG.
-
- 428 ERROR_VIO_NO_SAVE_RESTORE_THD
- No save/restore thread in SG.
-
- 429 ERROR_VIO_IN_BG
- Function invalid in background.
-
- 430 ERROR_VIO_ILLEGAL_DURING_POPUP
- Function not allowed during pop-up window.
-
- 431 ERROR_SMG_NOT_BASESHELL
- Caller is not the base shell.
-
- 432 ERROR_SMG_BAD_STATUSREQ
- Invalid status requested.
-
- 433 ERROR_QUE_INVALID_WAIT
- NoWait parameter out of bounds.
-
- 434 ERROR_VIO_LOCK
- Error returned from Scroll Lock.
-
- 435 ERROR_MOUSE_INVALID_IOWAIT
- Invalid parameters for IOWait.
-
- 436 ERROR_VIO_INVALID_HANDLE
- Invalid VIO handle.
-
- 437 ERROR_VIO_ILLEGAL_DURING_LOCK
- Function not allowed during screen lock.
-
- 438 ERROR_VIO_INVALID_LENGTH
- Invalid VIO length.
-
- 439 ERROR_KBD_INVALID_HANDLE
- Invalid KBD handle.
-
- 440 ERROR_KBD_NO_MORE_HANDLE
- Ran out of handles.
-
- 441 ERROR_KBD_CANNOT_CREATE_KCB
- Unable to create kcb.
-
- 442 ERROR_KBD_CODEPAGE_LOAD_INCOMPL
- Unsuccessful code-page load.
-
- 443 ERROR_KBD_INVALID_CODEPAGE_ID
- Invalid code-page identity.
-
- 444 ERROR_KBD_NO_CODEPAGE_SUPPORT
- No code page support.
-
- 445 ERROR_KBD_FOCUS_REQUIRED
- Keyboard focus required.
-
- 446 ERROR_KBD_FOCUS_ALREADY_ACTIVE
- Calling thread has an outstanding focus.
-
- 447 ERROR_KBD_KEYBOARD_BUSY
- Keyboard is busy.
-
- 448 ERROR_KBD_INVALID_CODEPAGE
- Invalid code page.
-
- 449 ERROR_KBD_UNABLE_TO_FOCUS
- Focus attempt failed.
-
- 450 ERROR_SMG_SESSION_NON_SELECT
- Session is not selectable.
-
- 451 ERROR_SMG_SESSION_NOT_FOREGRND
- Parent/child session is not foreground.
-
- 452 ERROR_SMG_SESSION_NOT_PARENT
- Not parent of requested child.
-
- 453 ERROR_SMG_INVALID_START_MODE
- Invalid session start mode.
-
- 454 ERROR_SMG_INVALID_RELATED_OPT
- Invalid session start related option.
-
- 455 ERROR_SMG_INVALID_BOND_OPTION
- Invalid session bond option.
-
- 456 ERROR_SMG_INVALID_SELECT_OPT
- Invalid session select option.
-
- 457 ERROR_SMG_START_IN_BACKGROUND
- Session started in background.
-
- 458 ERROR_SMG_INVALID_STOP_OPTION
- Invalid session stop option.
-
- 459 ERROR_SMG_BAD_RESERVE
- Reserved parameters are not zero.
-
- 460 ERROR_SMG_PROCESS_NOT_PARENT
- Session parent process already exists.
-
- 461 ERROR_SMG_INVALID_DATA_LENGTH
- Invalid data length.
-
- 462 ERROR_SMG_NOT_BOUND
- Parent is not bound.
-
- 463 ERROR_SMG_RETRY_SUB_ALLOC
- Retry request block allocation.
-
- 464 ERROR_KBD_DETACHED
- This call is not allowed for a detached PID.
-
- 465 ERROR_VIO_DETACHED
- This call is not allowed for a detached PID.
-
- 466 ERROR_MOU_DETACHED
- This call is not allowed for a detached PID.
-
- 467 ERROR_VIO_FONT
- No font is available to support the mode.
-
- 468 ERROR_VIO_USER_FONT
- User font is active.
-
- 469 ERROR_VIO_BAD_CP
- Invalid code page specified.
-
- 470 ERROR_VIO_NO_CP
- System displays do not support code page.
-
- 471 ERROR_VIO_NA_CP
- Current display does not support code page.
-
- 472 ERROR_INVALID_CODE_PAGE
- Invalid code page.
-
- 473 ERROR_CPLIST_TOO_SMALL
- Code page list is too small.
-
- 474 ERROR_CP_NOT_MOVED
- Code page was not moved.
-
- 475 ERROR_MODE_SWITCH_INIT
- Mode switch initialization error.
-
- 476 ERROR_CODE_PAGE_NOT_FOUND
- Code page was not found.
-
- 477 ERROR_UNEXPECTED_SLOT_RETURNED
- Internal error.
-
- 478 ERROR_SMG_INVALID_TRACE_OPTION
- Invalid start session trace indicator.
-
- 479 ERROR_VIO_INTERNAL_RESOURCE
- VIO internal resource error.
-
- 480 ERROR_VIO_SHELL_INIT
- VIO shell initialization error.
-
- 481 ERROR_SMG_NO_HARD_ERRORS
- No session manager hard errors.
-
- 482 ERROR_CP_SWITCH_INCOMPLETE
- DosSetProcessCp is unable to set a KBD or VIO code page.
-
- 483 ERROR_VIO_TRANSPARENT_POPUP
- Error during VIO pop-up window.
-
- 484 ERROR_CRITSEC_OVERFLOW
- Critical section overflow.
-
- 485 ERROR_CRITSEC_UNDERFLOW
- Critical section underflow.
-
- 486 ERROR_VIO_BAD_RESERVE
- Reserved parameter is not zero.
-
- 487 ERROR_INVALID_ADDRESS
- Invalid physical address.
-
- 488 ERROR_ZERO_SELECTORS_REQUESTED
- At least one selector must be requested.
-
- 489 ERROR_NOT_ENOUGH_SELECTORS_AVA
- Not enough GDT selectors to satisfy request.
-
- 490 ERROR_INVALID_SELECTOR
- Not a GDT selector.
-
- 491 ERROR_SMG_INVALID_PROGRAM_TYPE
- Invalid program type.
-
- 492 ERROR_SMG_INVALID_PGM_CONTROL
- Invalid program control.
-
- 493 ERROR_SMG_INVALID_INHERIT_OPT
- Invalid inherit option.
-
- 494 ERROR_VIO_EXTENDED_SG
-
-
- 495 ERROR_VIO_NOT_PRES_MGR_SG
-
-
- 496 ERROR_VIO_SHIELD_OWNED
-
-
- 497 ERROR_VIO_NO_MORE_HANDLES
-
-
- 498 ERROR_VIO_SEE_ERROR_LOG
-
-
- 499 ERROR_VIO_ASSOCIATED_DC
-
-
- 500 ERROR_KBD_NO_CONSOLE
-
-
- 501 ERROR_MOUSE_NO_CONSOLE
-
-
- 502 ERROR_MOUSE_INVALID_HANDLE
-
-
- 503 ERROR_SMG_INVALID_DEBUG_PARMS
-
-
- 504 ERROR_KBD_EXTENDED_SG
-
-
- 505 ERROR_MOU_EXTENDED_SG
-
-
- 506 ERROR_SMG_INVALID_ICON_FILE
-
-
- 507 ERROR_TRC_PID_NON_EXISTENT
-
-
- 508 ERROR_TRC_COUNT_ACTIVE
-
-
- 509 ERROR_TRC_SUSPENDED_BY_COUNT
-
-
- 510 ERROR_TRC_COUNT_INACTIVE
-
-
- 511 ERROR_TRC_COUNT_REACHED
-
-
- 512 ERROR_NO_MC_TRACE
-
-
- 513 ERROR_MC_TRACE
-
-
- 514 ERROR_TRC_COUNT_ZERO
-
-
- 515 ERROR_SMG_TOO_MANY_DDS
-
-
- 516 ERROR_SMG_INVALID_NOTIFICATION
-
-
- 517 ERROR_LF_INVALID_FUNCTION
-
-
- 518 ERROR_LF_NOT_AVAIL
-
-
- 519 ERROR_LF_SUSPENDED
-
-
- 520 ERROR_LF_BUF_TOO_SMALL
-
-
- 521 ERROR_LF_BUFFER_CORRUPTED
-
-
- 521 ERROR_LF_BUFFER_FULL
-
-
- 522 ERROR_LF_INVALID_DAEMON
-
-
- 522 ERROR_LF_INVALID_RECORD
-
-
- 523 ERROR_LF_INVALID_TEMPL
-
-
- 523 ERROR_LF_INVALID_SERVICE
-
-
- 524 ERROR_LF_GENERAL_FAILURE
-
-
- 525 ERROR_LF_INVALID_ID
-
-
- 526 ERROR_LF_INVALID_HANDLE
-
-
- 527 ERROR_LF_NO_ID_AVAIL
-
-
- 528 ERROR_LF_TEMPLATE_AREA_FULL
-
-
- 529 ERROR_LF_ID_IN_USE
-
-
- 530 ERROR_MOU_NOT_INITIALIZED
-
-
- 531 ERROR_MOUINITREAL_DONE
-
-
- 532 ERROR_DOSSUB_CORRUPTED
-
-
- 533 ERROR_MOUSE_CALLER_NOT_SUBSYS
-
-
- 534 ERROR_ARITHMETIC_OVERFLOW
-
-
- 535 ERROR_TMR_NO_DEVICE
-
-
- 536 ERROR_TMR_INVALID_TIME
-
-
- 537 ERROR_PVW_INVALID_ENTITY
-
-
- 538 ERROR_PVW_INVALID_ENTITY_TYPE
-
-
- 539 ERROR_PVW_INVALID_SPEC
-
-
- 540 ERROR_PVW_INVALID_RANGE_TYPE
-
-
- 541 ERROR_PVW_INVALID_COUNTER_BLK
-
-
- 542 ERROR_PVW_INVALID_TEXT_BLK
-
-
- 543 ERROR_PRF_NOT_INITIALIZED
-
-
- 544 ERROR_PRF_ALREADY_INITIALIZED
-
-
- 545 ERROR_PRF_NOT_STARTED
-
-
- 546 ERROR_PRF_ALREADY_STARTED
-
-
- 547 ERROR_PRF_TIMER_OUT_OF_RANGE
-
-
- 548 ERROR_PRF_TIMER_RESET
-
-
- 639 ERROR_VDD_LOCK_USEAGE_DENIED
-
-
- 640 ERROR_TIMEOUT
-
-
- 641 ERROR_VDM_DOWN
-
-
- 642 ERROR_VDM_LIMIT
-
-
- 643 ERROR_VDD_NOT_FOUND
-
-
- 644 ERROR_INVALID_CALLER
-
-
- 645 ERROR_PID_MISMATCH
-
-
- 646 ERROR_INVALID_VDD_HANDLE
-
-
- 647 ERROR_VLPT_NO_SPOOLER
-
-
- 648 ERROR_VCOM_DEVICE_BUSY
-
-
- 649 ERROR_VLPT_DEVICE_BUSY
-
-
- 650 ERROR_NESTING_TOO_DEEP
-
-
- 651 ERROR_VDD_MISSING
-
-
- 691 ERROR_IMP_INVALID_PARM
-
-
- 692 ERROR_IMP_INVALID_LENGTH
-
-
- 693 MSG_HPFS_DISK_ERROR_WARN
-
-
- 730 ERROR_MON_BAD_BUFFER
-
-
- 731 ERROR_MODULE_CORRUPTED
-
-
- 2055 ERROR_LF_TIMEOUT
-
-
- 2057 ERROR_LF_SUSPEND_SUCCESS
-
-
- 2058 ERROR_LF_RESUME_SUCCESS
-
-
- 2059 ERROR_LF_REDIRECT_SUCCESS
-
-
- 2060 ERROR_LF_REDIRECT_FAILURE
-
-
- 32768 ERROR_SWAPPER_NOT_ACTIVE
-
-
- 32769 ERROR_INVALID_SWAPID
-
-
- 32770 ERROR_IOERR_SWAP_FILE
-
-
- 32771 ERROR_SWAP_TABLE_FULL
-
-
- 32772 ERROR_SWAP_FILE_FULL
-
-
- 32773 ERROR_CANT_INIT_SWAPPER
-
-
- 32774 ERROR_SWAPPER_ALREADY_INIT
-
-
- 32775 ERROR_PMM_INSUFFICIENT_MEMORY
-
-
- 32776 ERROR_PMM_INVALID_FLAGS
-
-
- 32777 ERROR_PMM_INVALID_ADDRESS
-
-
- 32778 ERROR_PMM_LOCK_FAILED
-
-
- 32779 ERROR_PMM_UNLOCK_FAILED
-
-
- 32780 ERROR_PMM_MOVE_INCOMPLETE
-
-
- 32781 ERROR_UCOM_DRIVE_RENAMED
-
-
- 32782 ERROR_UCOM_FILENAME_TRUNCATED
-
-
- 32783 ERROR_UCOM_BUFFER_LENGTH
-
-
- 32784 ERROR_MON_CHAIN_HANDLE
-
-
- 32785 ERROR_MON_NOT_REGISTERED
-
-
- 32786 ERROR_SMG_ALREADY_TOP
-
-
- 32787 ERROR_PMM_ARENA_MODIFIED
-
-
- 32788 ERROR_SMG_PRINTER_OPEN
-
-
- 32789 ERROR_PMM_SET_FLAGS_FAILED
-
-
- 32790 ERROR_INVALID_DOS_DD
-
-
- 32791 ERROR_BLOCKED
-
-
- 32792 ERROR_NOBLOCK
-
-
- 32793 ERROR_INSTANCE_SHARED
-
-
- 32794 ERROR_NO_OBJECT
-
-
- 32795 ERROR_PARTIAL_ATTACH
-
-
- 32796 ERROR_INCACHE
-
-
- 32797 ERROR_SWAP_IO_PROBLEMS
-
-
- 32798 ERROR_CROSSES_OBJECT_BOUNDARY
-
-
- 32799 ERROR_LONGLOCK
-
-
- 32800 ERROR_SHORTLOCK
-
-
- 32801 ERROR_UVIRTLOCK
-
-
- 32802 ERROR_ALIASLOCK
-
-
- 32803 ERROR_ALIAS
-
-
- 32804 ERROR_NO_MORE_HANDLES
-
-
- 32805 ERROR_SCAN_TERMINATED
-
-
- 32806 ERROR_TERMINATOR_NOT_FOUND
-
-
- 32807 ERROR_NOT_DIRECT_CHILD
-
-
- 32808 ERROR_DELAY_FREE
-
-
- 32809 ERROR_GUARDPAGE
-
-
- 32900 ERROR_SWAPERROR
-
-
- 32901 ERROR_LDRERROR
-
-
- 32902 ERROR_NOMEMORY
-
-
- 32903 ERROR_NOACCESS
-
-
- 32904 ERROR_NO_DLL_TERM
-
-
- 65026 ERROR_CPSIO_CODE_PAGE_INVALID
-
-
- 65027 ERROR_CPSIO_NO_SPOOLER
-
-
- 65028 ERROR_CPSIO_FONT_ID_INVALID
-
-
- 65033 ERROR_CPSIO_INTERNAL_ERROR
-
-
- 65034 ERROR_CPSIO_INVALID_PTR_NAME
-
-
- 65037 ERROR_CPSIO_NOT_ACTIVE
-
-
- 65039 ERROR_CPSIO_PID_FULL
-
-
- 65040 ERROR_CPSIO_PID_NOT_FOUND
-
-
- 65043 ERROR_CPSIO_READ_CTL_SEQ
-
-
- 65045 ERROR_CPSIO_READ_FNT_DEF
-
-
- 65047 ERROR_CPSIO_WRITE_ERROR
-
-
- 65048 ERROR_CPSIO_WRITE_FULL_ERROR
-
-
- 65049 ERROR_CPSIO_WRITE_HANDLE_BAD
-
-
- 65074 ERROR_CPSIO_SWIT_LOAD
-
-
- 65077 ERROR_CPSIO_INV_COMMAND
-
-
- 65078 ERROR_CPSIO_NO_FONT_SWIT
-
-
- 65079 ERROR_ENTRY_IS_CALLGATE
-
-
- ΓòÉΓòÉΓòÉ 13. Specifications ΓòÉΓòÉΓòÉ
-
- Specifications covered:
-
- Γûá API Calls Made
-
- Γûá Memory Usage
-
- Γûá IX3 File Format
-
- Γûá DBF File Format
-
- Γûá DBT File Format
-
- Γûá Custom Sort-Compare Function
-
- Γûá Custom Build-Key
-
- Γûá Custom Expression Parser
-
-
- ΓòÉΓòÉΓòÉ 13.1. OS/2 API Calls Made ΓòÉΓòÉΓòÉ
-
- The following are the API calls made by Bullet.
-
- DosAllocMem DosClose DosCopy
- DosCloseMutexSem DosCreateDir DosCreateMutexSem
- DosDelete (*) DosErrClass DosExitList
- DosForceDelete DosFreeMem DosGetDateTime
- DosMapCase DosOpen DosQueryCollate
- DosQueryCp DosQueryCtryInfo DosQueryCurrentDisk
- DosQueryFSAttach DosQueryHType DosQuerySysInfo
- DosMove DosRead DosReleaseMutexSem
- DosRequestMutexSem DosResetBuffer DosScanEnv
- DosSetFileLocks DosSetFilePtr DosSetFileSize
- DosSetMaxFH DosSetRelMaxFH DosWrite
-
- (*) DosForceDelete is used in favor of DosDelete.
-
-
- ΓòÉΓòÉΓòÉ 13.2. Bullet Memory Usage ΓòÉΓòÉΓòÉ
-
- Memory is committed when allocated, using the PAG_COMMIT and the PAG_WRITE
- flags. This is memory allocated by Bullet itself. Additional memory needs are
- made by your code, such as parameter pack data, key buffers, and data record
- buffers.
-
- Code
-
- Bullet uses 7 pages for code, or less than 28KB.
-
- Data
-
- Γûá Shared Data
-
- A single page of shared memory is used by all Bullet processes.
-
- Γûá Instance Data
-
- One page of private memory is used by each Bullet processes.
-
- Γûá Handle Data
-
- One page of private memory is used by each open Bullet index file. For open
- data files, one page of private memory is used for files with 121 or fewer
- fields. Two pages are used for files with 249 or fewer fields. Three pages are
- used for files with 250 or more fields.
-
- For example, if one machine is running 2 Bullet processes, each with 10 open
- data files with 12 fields each, and 10 index files (one for each data file),
- its total memory usage is:
-
- Total code is 28KB.
- Shared data is 4KB.
- Instance data is 2 processes * 4KB, or 8KB (plus INIT_XB use shown
- below).
- DBF file data is 2 * 10 files * 4KB, or 80KB.
- Index file data is 2 * 10 files * 4KB, or 80KB.
-
- Total memory committed by Bullet for the above is 200KB, plus code and data of
- your two applications (or your single application, if the same application is
- being run twice). With no files open, for example when starting your program,
- only 40KB is committed. Thereafter, 4KB per open file. The memory is freed
- when the file is closed.
-
- Γûá Temporary Data
-
- Additional memory is allocated on a temporary basis, where the allocation is
- requested on entry to, and is freed upon exiting from, the routine called.
- INIT_XB's allocation can be considered permanent since INIT_XB is usually not
- exited until the program has ended. The following are the routines and the
- single requested amount:
-
- Routine Memory Allocated, in KB
-
- INIT_XB 8 for 1024 MAX_FILES version; 4KB for 100 and 250 MAX_FILES versions
- BACKUP_FILE_XB 8
- CREATE_DATA_XB 4 for 1-121 fields, 8 for 122-249 fields, 12 for 250+ fields
- CREATE_INDEX_XB 4
- PACK_RECORDS_XB adjustable, 128 default (less if file is smaller)
- REINDEX_XB adjustable, 144 default (minimum size is 48KB)
- UPDATE_XB varies: 40 + sum of record lengths where AP[].recNo!=0
-
- Γûá Stack Data
-
- Stack requirements are 8KB minimum for Bullet. No single stack allocation
- requests more than 4KB at a time (ie all changes to ESP (the CPU stack
- pointer) are less than 4KB at any one time), but some routines nest and
- require up to the minimum 8KB in total. The minimum recommended stack size
- for your Bullet application is 16KB. It's likely that you need to use a much
- larger size for your main program's stack use. If you have any doubt about
- stack space, double it, twice even.
-
-
- ΓòÉΓòÉΓòÉ 13.3. IX3 File Format ΓòÉΓòÉΓòÉ
-
- The IX3 index file is composed of a header followed by node data. The header
- layout is detailed below, followed by the node format.
-
- Index Header
-
- // nnn, where nnn is the offset of that item relative to the start of the file
-
- CHAR fileID[4]; // 000 file id = '31ch'
- ULONG nodeSize; // 004 size of a node, in bytes
- ULONG rootNode; // 008 root node (1-based)
- ULONG noKeys; // 012 total number of keys
- ULONG availNode; // 016 next available node (link to, 0 if none, 1-based)
- ULONG freeNode; // 020 next free node
- ULONG keyLength; // 024 length of key
- ULONG maxKeys; // 028 maximum number of keys on a node
- ULONG codePage; // 032 code page from CreateIndexFile
- ULONG countryCode; // 036 country code from CreateIndexFile
- ULONG sortFunction; // 040 system (1-9) or custom (10-19)
- // high word has flags: bit0=1 dups allowed
- // bit1-15 rez
-
- // Translated key expression as done by Parser during CreateIndex and Reindex.
- // More details on this is in the Custom Expression Parser Specifications.
- // For each key part in KH.expression a 4-byte structure is used in XLATEX:
-
- typedef struct _XLATEX {
- CHAR ftype; // field type (C,N,L, etc.),if bit7=1 and C then do UPPER key
- CHAR length; // bytes to use starting at offset (never > 64)
- SHORT offset; // byte offset into data record that length bytes are to be used
- } XLATEX;
-
- ULONG xlateCount; // 044 number of key fields (64/4=16 max fields)
- XLATEX xlateExpression[16]; // 048 key construct info (16 dword's worth)
- CHAR miscWorkspace[236]; // 112-347 B-tree workspace
- CHAR expression[160]; // 348 key expression, user (0-Terminated)
- ULONG CTsize; // 508 size of collate table following
- CHAR collateTable[256]; // 512 collate table, fill at CreateIndexXB
- CHAR rez[256]; // 768 to 1023 reserved (header size=1024 bytes)
-
- Node Data
-
- Directly after the header the node data starts. Each node is either 512, 1024,
- or 2048 bytes long. Each node contains a key count, indicating the number of
- active keys on the node, followed by key data.
-
- // nnn, where nnn is the offset of that item relative to the start of the node
-
- CHAR keyCount; // 000 1 to maxKeys (in header above)
- ULONG backNode; // 001 previous node page, or 0 if this node is a leaf
- XNODE node[maxKeys]; // 005...
-
- For each key on the node:
-
- typedef struct _XNODE {
- CHAR keyValue[keyLength]; // 005 actual key (keyLength from header)
- ULONG recordNo; // 005+keyLength record number for key
- ULONG fwdNode; // 005+keyLength+4 next node page, or 0 if leaf
- } XNODE;
-
- backNode and fwdNode are node numbers. The first node is 1, and is located
- directly after the header. The last node used is at header:freeNode-1. Each
- fwdNode of a key is also the next key's backNode. If the node has had all keys
- removed, its node number is placed on the top of the header:availNode list, and
- the first 4 bytes of the node are used as a link to the previous list top.
-
-
- ΓòÉΓòÉΓòÉ 13.4. DBF File Format ΓòÉΓòÉΓòÉ
-
- The DBF data file is composed of a header, field descriptors, one per field,
- and the actual record data. The header layout is detailed below, followed by
- the field descriptor layout and then the description of the data record.
-
- DBF Header
-
- // nnn, where nnn is the offset of that item relative to the start of the file
-
- CHAR fileID; // 000 file id byte
- CHAR lastUpdateYR; // 001 binary year-1900
- CHAR lastUpdateMO; // 002 binary month (1-12)
- CHAR lastUpdateDA; // 003 binary day (1-31)
- ULONG noRecords; // 004 total number of records
- SHORT headerLength; // 008 length of data header
- SHORT recordLength; // 010 record length
- SHORT nada; // 012 reserved
- CHAR xactionFlag; // 014 flag indicating incomplete dBASE transaction (n/a)
- CHAR encryptFlag; // 015 flag indicating encrypted (n/a)
- CHAR filler[16]; // 016 fill to 32 bytes
-
- Field Descriptors
-
- For each field, a descriptor is stored in the DBF. The first descriptor starts
- directly after the header, at file offset 32 (the 33rd byte). Each descriptor
- is 32 bytes. After the last descriptor, a byte with ASCII value 13 (0x0D) is
- stored. Following this byte, the record data starts.
-
- // nnn, where nnn is offset of item relative to the start of the descriptor
-
- CHAR fieldName[11]; // 000 ASCII, UPPER, underscore, zero-filled, (0T)
- CHAR fieldType; // 011 UPPER C,N,D,L,M
- ULONG fieldDA; // 012 not used
- CHAR fieldLength; // 016 1-255 bytes, depending on fieldType
- CHAR fieldDC; // 017 places right of decimal point
- SHORT altFieldLength; // 018 alternate field length when fieldLength==0
- CHAR filler[12]; // 020 not used
-
- // altFieldLength is proprietary to Bullet, and can be used if Xbase
- // compatibility is not required and fields need to be larger than 255 bytes.
- // To use it, set fieldLength=0 and altFieldLength to > 255 bytes.
-
- Record Data
-
- The DBF data are free-form, fixed-length records. Each data record starts with
- a one-byte 'tag' field, which is implicitly defined for all records (hence, it
- is not a formal field and has no descriptor). Following the tag field is the
- first field of the record, and following that field (whose length is described
- in the field's descriptor) is the next field, and so on. No separators are
- used between fields. After the very last data record in the file, DBF
- specification dictates that an end of file marker be placed, so at the end of
- the file is a byte of value ASCII 26 (0x1A).
-
- Record layout is as you define in your application. It must match the layout
- as described in the field descriptors, byte-for-byte.
-
- Increasing DBF Performance
-
- Records are stored in the order they were written. To improve performance,
- especially indexed-sequential access, the data file may be sorted, or
- clustered, by reading each record in primary key order, then writing that
- record to a new DBF data file. Repeat for each record. After all records have
- been written, reindex the newly created DBF data file (and all related index
- files). After this, delete the old files (data and index), and rename the new
- ones to the filenames required. This technique maximizes cache efficiency, and
- can easily offer 10x performance increase in access speed.
-
-
- ΓòÉΓòÉΓòÉ 13.5. DBT File Format ΓòÉΓòÉΓòÉ
-
- The DBT memo file is composed of a header followed by memo data, stored in one
- or more blocks. The header layout is detailed below, followed by the memo
- record.
-
- DBT Header
-
- // nnn, where nnn is the offset of that item relative to the start of the file
-
- ULONG memoAvailBlock; // 000 next available block (header is block 0)
- ULONG memoRez; // 004 not used
- CHAR memoFilename[8]; // 008 filename proper (first 8 of filename proper)
- ULONG memoRez2; // 016 not used (apparently)
- ULONG memoBlockSize; // 020 block size, must be at least 24
-
- // the rest of the header block (to block size bytes) is unused
-
- Memo Record
-
- // nnn, where nnn is offset of item relative to the start of the memo record
-
- ULONG memoAvail; // 000 next available link
- ULONG memoSize; // 004 size of data (including this and memoAvail)
- CHAR memoData; // 008 for as many bytes as memoSize, less 8
-
- A memo may use one or more blocks (each block is a fixed size), but allocations
- are always contiguous. Unused bytes after the memo data (to the end of the
- last block allocated to that memo record) are undefined. memoAvail is 0x8FFFF
- for all active memo records. For deleted memo records, memoAvail is used as a
- link in the memoAvail list. memoSize is the total bytes used by the memo,
- including the memoAvail and memoSize data, so it is the size of the real data +
- 8 bytes.
-
-
- ΓòÉΓòÉΓòÉ 13.6. Custom Sort-Compare Function ΓòÉΓòÉΓòÉ
-
- Bullet provides 10 custom sort-compare functions, in addition to the 6
- intrinsic sort-compare functions (ASCII, NLS, and the four integer compares).
- The custom function you supply is not actually a sort function, as the name
- implies, but a compare function. Basically, two strings are supplied and your
- function determines string1's relation to string2 (<, >, or ==).
-
- The strings supplied (via pointers) are not C strings, and they are not
- (necessarily) 0-terminated. A count value is passed, indicating the number of
- bytes to compare. The handle of the index file for which this compare is being
- done is also supplied, so that you can interrogate the index file state
- (STAT_INDEX_XB) for any additional information required.
-
- In addition to the compare function this routine performs, a special-case call
- is made to this routine requesting a pointer to a string of HIGH-VALUES for
- this sort compare. The pointer must be to a static memory area that exists for
- as long as the index file is open, and must be at least as long as count. This
- special-case call is indicated by both string pointers==NULL.
-
- To use a custom sort-compare function, first use SET_SYSVARS_XB to assign the
- custom sort ID (10 to 19) with the function's address pointer. Once assigned,
- an index file may be created with its CIP.sortFunction set to the sort ID
- (10-19). Also, any previously created index file with a custom sort ID may now
- be opened (but only after you used SET_SYSVARS_XB to assign the sort-compare
- function pointer). During the index file create, the sort ID you specified for
- the create is stored in the index file. When that index file is later opened,
- that same sort ID is used, and so requires that the custom sort-compare
- function already be assigned (with SET_SYSVARS_XB) before opening the index
- file. This means that you need to be consistent in your custom sort ID
- numbering, since each index created forever uses that sort ID you specified.
-
- It's simple to create a custom sort-compare function. The calling convention
- is APIENTRY (or _System, or __syscall for some compilers), and the parameters
- are passed to your function on the stack (by Bullet). A sample prototype for a
- custom sort-compare function follows:
-
- LONG APIENTRY YourCustomSort10(PVOID str1,
- PVOID str2,
- ULONG count,
- ULONG handle);
-
- If the pointers are not NULL, your routine is to compare str1 to str2, for
- count bytes, and is to return:
-
- -1 if str1 is less than str2
- 0 if str1 is equal to str2
- 1 if str1 is greater than str2
-
- str is not a C string, but is of type void. Cast as required,
- depending on the data expected.
-
- If str1 and str2 are both NULL, your routine must return a pointer to a static
- object that contains high-values for the object type. For example, if the
- sort-compare is for IEEE floating-point, then the function is to return a
- pointer to a static data area filled with the highest floating-point value.
- Depending on your sort-compare routine's functionality, you may need just a
- single high-value, or multiple high-values, one after the other (e.g., if you
- are supporting compound key values for binary keys). The count parameter
- indicates the total bytes needed, so divide by the object size to get the
- number of objects required. Be aware that the object size (in count) is +2
- bytes for the enumerator if DUPS_ALLOWED was specified when the index file was
- created. This high-values object is used in the REINDEX_XB routine, and also
- the LAST_KEY_XB and GET_LAST_XB routines.
-
-
- ΓòÉΓòÉΓòÉ 13.7. Custom Build-Key ΓòÉΓòÉΓòÉ
-
- Bullet provides an internal build-key routine that constructs the key from the
- data record supplied. The internal routine can be overloaded by your custom
- build-key routine if you need additional functionality. It may be used in
- conjunction with a custom sort-compare function, or an intrinsic Bullet
- sort-compare.
-
- Developing a custom build-key routine requires delving into the internal Bullet
- data structures. It is more complicated than a custom sort-compare function,
- but not really any more complex. The handle of the index file is passed, and
- using this, STAT_INDEX_XB is called to get the SIP.herePtr pointer. This is
- the pointer to the internal Bullet data structure for this index file. What
- needs to be accessed in this structure is the translated key expression. From
- this, you have the starting offset in the data record, and the byte count to
- use, for each key component (up to 16 components per key). The offset value as
- stored in the XLATEX structure does not include the tag field byte. Therefore,
- to locate to the correct offset, add 1 to the value in offset. For example,
- XLATEX.offset=0 means to use the first field, which is the first byte after the
- tag field byte, but the physical offset, as referenced to recPtr, is not at
- offset=0, but is at offset=1.
-
- This translated key expression structure is:
-
- // (This is an excerpt from the IX3 header format)
-
- // Translated key expression as done by Parser during CreateIndex and Reindex.
- // More details on this is in the Custom Expression Parser Specifications.
- // For each key part in KH.expression a 4-byte structure is used:
-
- typedef struct _XLATEX {
- CHAR ftype; // field type (C,N,L, etc.),if bit7=1 and C then do UPPER key
- CHAR length; // bytes to use starting at offset (never > 64)
- SHORT offset; // byte offset into data record that length bytes are to be used
- } XLATEX; // (note that offset does not count tag field byte)
-
- ULONG xlateCount; // 044 number of key fields (64/4=16 max fields)
- XLATEX xlateExpression[16]; // 048 key construct info (16 dword's worth)
-
- xlateExpression is at offset +48 relative the IX3 index header. However,
- SIP.herePtr points to -384 relative the IX3 index header start. Therefore, to
- locate to xlateExpression, you must add 384 to 48. This means that
- xlateExpression[0].ftype is located at SIP.herePtr+432. The number of valid
- key components in xlateExpression is stored in xlateCount (at SIP.herePtr+428).
-
- The calling convention for your custom build-key function is APIENTRY (or
- _System, or __syscall for some compilers), and the parameters are passed to
- your function on the stack (by Bullet). A sample prototype for a build-key
- function follows:
-
- ULONG APIENTRY YourBuildKey(ULONG handle,
- PVOID recordPtr,
- PVOID keyPtr,
- PULONG keyLenPtr
- PULONG sortFuncPtr);
-
- Using the data from xlateExpression, you are to build a key from the data
- record located at the passed pointer, recordPtr, and are store the built key in
- the buffer located at keyPtr. For each key component, you copy from the data
- record xlateExpression[].length bytes starting at xlateExpression[].offset+1
- (given the 1-byte tag field which is not accounted for otherwise), and build
- other key components after previously build parts. If the index file allows
- duplicate keys (DUPS_ALLOWED is flagged in SIP.sortFunction), then append an
- enumerator to the end of the key proper. The handle of the index file is
- passed, which is used when calling STAT_INDEX_XB (to get SIP.herePtr). The
- return is 0 if successful, or an appropriate Bullet error code (EXB_) should be
- used. In addition, the key length is placed in the ULONG data pointed to by
- keyLenPtr (SIP.keyLength may be used), and the sort-compare function is placed
- in the ULONG data pointed to by sortFuncPtr (SIP.sortFunction may be used).
-
- The routine is also to check if the tag field of the data record matches the
- skip-tag value, as set by SET_SYSVARS_XB. If the tag field matches,
- WRN_SKIP_KEY is to be returned as the 'error' code. The key is built
- regardless of a match.
-
-
- ΓòÉΓòÉΓòÉ 13.8. Custom Expression Parser ΓòÉΓòÉΓòÉ
-
- Bullet provides an internal key expression parser routine that constructs the
- translated key expression stored in the index file header. The internal
- routine can be overloaded by your custom expression parser routine if you need
- additional functionality. It may be used in conjunction with a custom
- sort-compare function, with a custom build-key routine, or with an intrinsic
- Bullet sort-compare.
-
- Developing a custom expression parser routine requires delving into the
- internal Bullet data structures. It is more complicated than a custom
- sort-compare function, and it is also much more complex. Unlike the custom
- sort-compare and build-key functions, no handle is passed to the parser. This
- is because, rather than using the handle to get the SIP.herePtr, this pointer
- is passed directly to this routine. This is the pointer to the internal Bullet
- data structure for this index file. What needs to be accessed in this
- structure is the translated key expression location, as well as the text
- version of the key expression, as supplied by the programmer/user. To the
- XLATEX data you place the starting offset in the data record, and the byte
- count to use, for each key component you parse from the key expression (up to
- 16 components per key). The offset value as stored in the XLATEX structure
- does not include the tag field byte. Therefore, the correct offset to store is
- the physical offset within the record, minus 1. For example, XLATEX.offset=0
- should be used for the offset of the first field, which is the first byte after
- the tag field byte. For each component parsed, an XLATEX data structure is
- added to the xlateExpression data area (up to 16). Unused XLATEX components
- must be set to 0. When all components have been stored, the xlateCount value
- is set to the number of key components stored.
-
- DHDptr is the data header pointer. It is -352 bytes relative the DBF data
- header. However, rather than using absolute addressing to locate field
- descriptor data (needed for parsing), it's recommended that the DBF handle be
- obtained from the KHptr structure. Since no file handles are passed, you must
- read the xbLink handle value from the index file header. The xbLink handle is
- stored at KHptr+12. With this handle, you call the GET_DESCRIPTOR_XB routine
- to obtain field descriptor info for each field.
-
- This translated key expression structure, and text expression location, is at:
-
- // (This is an excerpt from the IX3 header format)
-
- // Translated key expression as done by Parser during CreateIndex and Reindex.
- // For each key part in KH.expression a 4-byte structure is used:
-
- typedef struct _XLATEX {
- CHAR ftype; // field type (C,N,L, etc.),if bit7=1 and C then do UPPER key
- CHAR length; // bytes to use starting at offset (never > 64)
- SHORT offset; // byte offset into data record that length bytes are to be used
- } XLATEX; // (note that offset does not count tag field byte)
-
- ULONG xlateCount; // 044 number of key fields (64/4=16 max fields)
- XLATEX xlateExpression[16]; // 048 key construct info (16 dword's worth)
- : // 112-347 :
- CHAR expression[160]; // 348 key expression, user (0-Terminated)
-
- xlateExpression is at offset +48 relative the IX3 index header. However,
- KHptr, passed to this routine, points to -384 relative the IX3 index header
- start. Therefore, to locate to xlateExpression, you must add 384 to 48. This
- means that xlateExpression[0].ftype is located at KHptr+432. The number of
- valid key components in xlateExpression is stored in xlateCount (at KHptr+428).
- To text key expression string, which you are to parse, is located at KHptr+732.
- This is identical to the expression passed during CREATE_INDEX_XB (and it is
- CREATE_INDEX_XB that calls this parser routine).
-
- The calling convention for your custom key expression parser function is
- APIENTRY (or _System, or __syscall for some compilers), and the parameters are
- passed to your function on the stack (by Bullet). A sample prototype for a
- build-key function follows:
-
- ULONG APIENTRY YourKeyExpressionParser(PVOID DHDptr,
- PVOID KHptr,
- PULONG keyLenPtr);
-
- You are to parse the text key expression at KHptr+732 and store the key
- component XLATEX structure values to the XLATEX structure, one for each key
- component parsed. In addition, the key length (the sum of the XLATEX.length
- fields) is placed in the ULONG data pointed to by keyLenPtr. The keylength may
- not exceed 64 bytes. If DUPS_ALLOWED is flagged, add two to the sum of the
- XLATEX.length fields for the enumerator word.
-
- Note: The key expression has been mapped to upper-case and 0-filled by the
- time this routine is called.
-
- This is probably the most difficult part of customizing Bullet. However, the
- difficulty lies not with Bullet, but how you parse. The idea is simple -- you
- are to generate a xlateCount value, and for each key component (ie
- non-contiguous, non-same-type run in the data record), an XLATEX variable
- describing the method to build that key component out of the data record (type,
- length, and starting offset) is stored. The text key expression is available
- in the index header, and the destination to write to is there, also. You do
- need to read the index header at KHptr+12 (ULONG) to obtain the DBF handle for
- this index file before you can parse the expression. This because you need to
- know about the record field names, types, and lengths before you can parse the
- key expression. The matter not covered here is that of parsing the expression,
- which is left to the programmer. Any lexical parser algorithm may be used, or
- you may even do no parsing at all, and simply hard-code values into the XLATEX
- structures.
-
- If you've gotten this far, you may find the following data structures useful.
- The numbers at // nnn are offsets relative the SIP.herePtr and SDP.herePtr
- pointers. For example, at SIP.herePtr+352 is a ULONG of the number of key
- searches requested. These could be monitored in a separate thread.
-
- Relative SIP.herePtr:
-
- ULONG fType; // 000 bit0=0 for index file, btree
- ULONG flags; // 004 bit0=1 is dirty
- // bit1=1 full lock (count stored in KH.lockCount)
- // bit2=1 shared lock (if bit1=1)
- // bit3-14 reserved (=0)
- // bit15=1 no coalesce on key delete
- // 006 BYTE, progress of reindex (0,1-99)
- // 007 BYTE
- PVOID morePtr; // 008 ptr to additional header info, if ever needed
- ULONG xbLink; // 012 related XB data file handle
- ULONG asMode; // 016 access-sharing-cache mode of open
- CHAR filename[260]; // 020 filename at open (0T)
- ULONG currKeyRecNo; // 280 current rec number assigned to KH.currKey
- CHAR currKey[64]; // 284 current key value
- ULONG rez0; // 348 allow for 0-terminated string
- ULONG searches; // 352 keys searched for
- ULONG seeks; // 356 nodes seeked
- ULONG hits; // 360 seeks satisfied without disk access
- ULONG keysDeleted; // 364 keys deleted since last zeroed
- ULONG keysStored; // 368 keys added since last zeroed
- ULONG nodesSplit; // 372 splits needed on insert since last zeroed
- ULONG nodesMadeAvail; // 376 nodes made available from deleting keys
- ULONG lockCount; // 380 active full-lock count
-
- // the IX3 index header follows at 384+
-
- Relative SDP.herePtr:
-
- ULONG fType; // 000 bit0=1 for DBF data file, XB
- ULONG flags; // 004 bit0=1 is dirty
- // bit1=1 full lock
- // bit2=1 shared lock (if bit1=1)
- // bit3-15 reserved (=0)
- // 006 BYTE, progress of pack (0,1-99)
- // 007 BYTE, 0
- PVOID morePtr; // 008 ptr to additional header info, if ever needed
- ULONG noFields; // 012 number of fields in this data file
- ULONG asMode; // 016 access-sharing-cache mode of open
- CHAR filename[260]; // 020 filename at open (0T)
- ULONG lockCount; // 280 only when dec'ed to 0 do full unlock
- ULONG memoAvailBlock; // 284 next available block (header is block 0)
- ULONG memoUnk1; // 288 not used
- CHAR memoFilename[8]; // 292 filename proper (first 8 of filename proper)
- ULONG memoUnk2; // 300 not used (apparently)
- ULONG memoBlockSize; // 304 block size, must be at least 24 to cover header!
- ULONG memoHandle; // 308 handle of open memo file
- ULONG memoFlags; // 312 bit0=1 is dirty
- ULONG memoLastNo; // 316 last accessed memo number (if not 0)
- ULONG memoLastLink; // 320 link data for last accessed memo
- ULONG memoLastSize; // 324 size of last accessed memo (in bytes, w/OH)
- ULONG align32[6]; // 328 (align to even32)
-
- // the DBF data header follows at +352
-
-
- ΓòÉΓòÉΓòÉ 14. Bullet Is... ΓòÉΓòÉΓòÉ
-
- Bullet/2 is a thread-safe, multi-process capable database engine toolkit for
- OS/2. It provides pre-built and tested access methods to data and index files
- for application programmers. It is not an end-user Database Management System
- (DBMS), but it is a tool that could be used to develop one. Bullet is compact,
- efficient, and very fast. It can be configured to use custom key-build,
- sort-compare functions, and expression parser routines to extend the built-in
- functionality. Rules are few; possibilities are great.
-
- The standard data format is DBF (dBASE 3+ and later). The supported memo
- format is DBT (dBASE 4 and later). Index-only support can be enabled and with
- this any data file format may be used (the data maintained by the programmer
- then). Also, the DBF standard may be extended by using binary field values and
- fields larger than 255 bytes. Index files are NLS-compatible and use an
- efficient b-tree structure. Files may be any size supported by the OS, up to
- 4GB. Up to 1024 files may be opened and in use by any one process, with any
- number of processes active.
-
- The Bullet API consists of a wide assortment of routines, from low-level OS
- calls to high-level transaction-list routines that can process hundreds of
- files per transaction, with roll-back on error. Network and multi-user support
- is included, and makes use of operating system features such as atomic
- re-locking, and shared locks that allow other processes read-access to locked
- files.
-
- Bullet is simple to use, and may easily be modified by using function wrappers
- around groups of related Bullet routines. If you don't like working with the
- parameter packs, use a wrapper and call as you like. Bullet works the way you
- are used to working.
-
- This online manual is a complete introduction and programmer reference for
- Bullet/2. Sample code is included, with more still on disk. Use Bullet to
- make a mint, or at least that database program that OS/2 users are looking for.
- I'd like to, but then I wouldn't be selling Bullet to you if I were.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- A foreign key is a column value in one table used as a primary key in a second
- table. For example, if one table has two fields: employee name (primary key)
- and department code, and a second table has two fields: department code
- (primary key) and department name, then department code in the first table is
- considered a foreign key, since it may be used as the primary key value when
- searching the second table. Joins and views make use of foreign keys.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Null pack
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _ACCESSPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file to access */
- LONG recNo; /* IO, record number */
- PVOID recPtr; /* I, programmer's record buffer */
- PVOID keyPtr; /* I, programmer's key buffer */
- PVOID nextPtr; /* I, NULL if not xaction, else next AP in list */
- } ACCESSPACK; /* AP */
- typedef ACCESSPACK *PACCESSPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _COPYPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file to copy */
- PSZ filenamePtr; /* I, filename to use (drv:path must exist if used) */
- } COPYPACK; /* CP */
- typedef COPYPACK *PCOPYPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _CREATEDATAPACK {
- ULONG func;
- ULONG stat;
- PSZ filenamePtr; /* I, filename to use */
- ULONG noFields; /* I, 1 to 254 */
- PFIELDDESCTYPE fieldListPtr; /* I, descriptor list, 1 per field */
- ULONG fileID; /* I, 0x03 for std DBF, 0x8B for DBF+DBT */
- } CREATEDATAPACK; /* CDP */
- typedef CREATEDATAPACK *PCREATEDATAPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _CREATEINDEXPACK {
- ULONG func;
- ULONG stat;
- PSZ filenamePtr; /* I, filename to use */
- PSZ keyExpPtr; /* I, e.g., "SUBSTR(LNAME,1,4)+SSN" */
- LONG xbLink; /* I, opened data file handle this indexes */
- ULONG sortFunction; /* I, 1-9 system, 10-19 custom */
- ULONG codePage; /* I, 0=use process default */
- ULONG countryCode; /* I, 0=use process default */
- PVOID collatePtr; /* I, NULL=use cc/cp else use passed table for sort */
- ULONG nodeSize; /* I, 512, 1024, or 2048 */
- } CREATEINDEXPACK; /* CIP */
- typedef CREATEINDEXPACK *PCREATEINDEXPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _FIELDDESCTYPE {
- BYTE fieldName[11]; /* IO, upper A-Z and _; 1-10 chars, 0-filled, 0-term */
- BYTE fieldType; /* IO, C,D,L,N, or M */
- LONG fieldDA; /* x, offset within record (run-time storage option) */
- BYTE fieldLen; /* IO, C=1-255,D=8,L=1,N=1-19,M=10 */
- BYTE fieldDC; /* IO, fieldType=N then 0-15 else 0 */
- USHORT altFieldLength;/* IO, 0 */
- BYTE filler[12]; /* I, 0 */
- } FIELDDESCTYPE; /* nested in DESCRIPTORPACK */
- typedef FIELDDESCTYPE *PFIELDDESCTYPE;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _DESCRIPTORPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of DBF file */
- ULONG fieldNumber; /* IO, first field is 1 */
- ULONG fieldOffset; /* O, offset of field within record (tag=offset 0) */
- FIELDDESCTYPE FD; /* IO FD.fieldName, O rest of FD */
- } DESCRIPTORPACK; /* DP */
- typedef DESCRIPTORPACK *PDESCRIPTORPACK;
- };
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _DOSFILEPACK {
- ULONG func;
- ULONG stat;
- PSZ filenamePtr; /* I, filename to use */
- ULONG handle; /* IO, handle of open file */
- ULONG asMode; /* I, access-sharing-cache mode */
- ULONG bytes; /* IO, bytes to read, write, length of */
- LONG seekTo; /* IO, seek to offset, current offset */
- ULONG method; /* I, seek method (0=start of file, 1=current, 2=end) */
- PVOID bufferPtr; /* I, buffer to read into or write from */
- ULONG attr; /* I, attribute to create file with */
- PSZ newNamePtr; /* I, name to use on rename */
- } DOSFILEPACK; /* DFP */
- typedef DOSFILEPACK *PDOSFILEPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _EXITPACK {
- ULONG func;
- ULONG stat;
- } EXITPACK; /* EP */
- typedef EXITPACK *PEXITPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _HANDLEPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file */
- } HANDLEPACK; /* HP */
- typedef HANDLEPACK *PHANDLEPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _INITPACK {
- ULONG func;
- ULONG stat;
- ULONG JFTsize; /* I, max opened files (20-1024+) */
- ULONG versionDOS; /* O, e.g., 230 for 2.30 */
- ULONG versionBullet; /* O, e.g., 2019 for 2.019 */
- ULONG versionOS; /* O, e.g., 4=OS/2 32-bit */
- PVOID exitPtr; /* O, function pointer to EXIT_XB routine */
- } INITPACK; /* IP */
- typedef INITPACK *PINITPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _LOCKPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of Bullet file to lock */
- ULONG xlMode; /* I, index lock mode (0=exclusive, 1=shared) */
- ULONG dlMode; /* I, data lock mode (0=exclusive, 1=shared) */
- LONG recStart; /* I, if data, first record # to lock, or 0 for all */
- ULONG recCount; /* I, if data and recStart!=0, # records to lock */
- PVOID nextPtr; /* I, NULL if not xaction, else next LP in list */
- } LOCKPACK; /* LP */
- typedef LOCKPACK *PLOCKPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _MEMODATAPACK {
- ULONG func;
- ULONG stat;
- ULONG dbfHandle; /* I, handle of DBF file to which this memo file belongs */
- ULONG memoBypass; /* I, memo bypass function to do, if any */
- PVOID memoPtr; /* I, ptr to memo record buffer */
- ULONG memoNo; /* IO, memo record number (aka block number) */
- ULONG memoOffset; /* I, position within record to start read/update */
- ULONG memoBytes; /* IO, number of bytes to read/update */
- } MEMODATAPACK; /* MDP */
- typedef MEMODATAPACK *PMEMODATAPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _MEMORYPACK {
- ULONG func;
- ULONG stat;
- ULONG memory; /* O, not used in OS/2 */
- } MEMORYPACK; /* MP */
- typedef MEMORYPACK *PMEMORYPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _OPENPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* O, handle of file opened */
- PSZ filenamePtr; /* I, Bullet file to open */
- ULONG asMode; /* I, access-sharing-cache mode */
- LONG xbLink; /* I, if index open, xbLink=handle of its opened DBF */
- } OPENPACK; /* OP */
- typedef OPENPACK *POPENPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _QUERYSETPACK {
- ULONG func;
- ULONG stat;
- ULONG item; /* I, Bullet sysvar item to get/set */
- ULONG itemValue; /* IO, current/new value */
- } QUERYSETPACK; /* QSP */
- typedef QUERYSETPACK *PQUERYSETPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _REMOTEPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle of file, or if 0, use RP.drive */
- ULONG drive; /* I, drive (1=A,2=B,3=C,...0=current) to check */
- ULONG isRemote; /* O, =1 of handle/drive is remote, =0 if local */
- ULONG flags; /* O, 0 */
- ULONG isShare; /* O, 1 */
- } REMOTEPACK; /* RP */
- typedef REMOTEPACK *PREMOTEPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- struct StatDataPack {
- typedef struct _STATDATAPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle to check */
- ULONG fileType; /* O, bit0=1 data file */
- ULONG flags; /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
- ULONG progress; /* O, 0,1-99% pack progress */
- PVOID morePtr; /* O, 0 */
- ULONG fields; /* O, fields per record */
- ULONG asMode; /* O, access-sharing-cache mode */
- PSZ filenamePtr; /* O, filename used in open */
- ULONG fileID; /* O, first byte of DBF file */
- ULONG lastUpdate; /* O, high word=year,low byte=day, high byte=month */
- ULONG records; /* O, data records (including "deleted") */
- ULONG recordLength; /* O, record length */
- ULONG xactionFlag; /* O, 0 */
- ULONG encryptFlag; /* O, 0 */
- PVOID herePtr; /* O, this file's control address */
- ULONG memoHandle; /* O, handle of open memo file (0 if none) */
- ULONG memoBlockSize; /* O, memo file block size */
- ULONG memoFlags; /* O, bit0=1 dirty */
- ULONG memoLastRecord; /* O, last accessed memo record (0 if none) */
- ULONG memoLastSize; /* O, size of last accessed memo record (in bytes, +8) */
- ULONG lockCount; /* O, number of full-locks in force */
- } STATDATAPACK; /* SDP */
- typedef STATDATAPACK *PSTATDATAPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _STATHANDLEPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle to check */
- LONG ID; /* O, bit0=1 data file, bit0=1 index file */
- } STATHANDLEPACK; /* SHP */
- typedef STATHANDLEPACK *PSTATHANDLEPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _STATINDEXPACK {
- ULONG func;
- ULONG stat;
- ULONG handle; /* I, handle to check */
- ULONG fileType; /* O, bit0=0 index file */
- ULONG flags; /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
- ULONG progress; /* O, 0,1-99% reindex progress */
- PVOID morePtr; /* O, 0 */
- ULONG xbLink; /* O, XB file link handle */
- ULONG asMode; /* O, access-sharing-cache mode */
- PSZ filenamePtr; /* O, pointer to filename used in open */
- ULONG fileID; /* O, "31ch" */
- PSZ keyExpPtr; /* O, pointer to key expression */
- ULONG keys; /* O, keys in file */
- ULONG keyLength; /* O, key length */
- ULONG keyRecNo; /* O, record number of current key */
- PVOID keyPtr; /* O, ptr to current key value (valid to keyLength) */
- PVOID herePtr; /* O, this file's control address */
- ULONG codePage; /* O, code page at create time */
- ULONG countryCode; /* O, country code at create time */
- PVOID CTptr; /* O, collate table ptr, NULL=no collate table present */
- ULONG nodeSize; /* O, node size */
- ULONG sortFunction; /* O, sort function ID */
- ULONG lockCount; /* O, number of full-locks in force */
- } STATINDEXPACK; /* SIP */
- typedef STATINDEXPACK *PSTATINDEXPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- typedef struct _XERRORPACK {
- ULONG func;
- ULONG stat; /* I, error to check */
- ULONG errClass; /* O, class of error */
- ULONG action; /* O, action recommended for error */
- ULONG location; /* O, location of error */
- } XERRORPACK; /* XEP */
- typedef XERRORPACK *PXERRORPACK;
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Access-Mode (required)
- READONLY 0x00000000 read-only access open
- WRITEONLY 0x00000001 write-only access open
- READWRITE 0x00000002 read/write access open
-
- Share-Mode (required)
- DENYREADWRITE 0x00000010 no other process may share file
- DENYWRITE 0x00000020 no other process may share file for write
- DENYREAD 0x00000030 no other process may share file for read
- DENYNONE 0x00000040 any process may share file
-
- Inherit
- NOINHERIT 0x00000080 child process does not inherit file handles
-
- Cache
- NO_LOCALITY 0x00000000 locality is not known
- SEQ_LOCALITY 0x00010000 access will be mainly sequential
- RND_LOCALITY 0x00020000 access will be mainly random
- MIX_LOCALITY 0x00030000 access will be random with some sequential
- SKIP_CACHE 0x00100000 I/O is not to go through the cache
- WRITE_THROUGH 0x00400000 control returns only after disk is written to
-
- Access- and Share-Mode values not explicitly listed are not valid. The file
- access mode is a combination of ACCESS + SHARE + INHERIT + CACHE. Typical data
- and index asMode is 0x00000042, though locality may be set accordingly (e.g.,
- 0x00020042 for mostly random access to the file).
-
- The Cache mode options are valid for OPEN_DATA_XB and OPEN_INDEX_XB only; for
- DOS_FILE_OPEN, the Cache values must be right-shifted by 8. The 'Skip Cache'
- and 'Write Through' options are not inherited.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- The enumerator is a big-endian 16-bit value that serves to differentiate up to
- 65536 "identical", non- unique keys. It is attached to all keys of
- DUPS_ALLOWED flagged index files (set at CREATE_INDEX_XB), and occupies the
- last two bytes of the key. The first key of the type uses \0\0, the second
- uses \0\1, and so on. This ordering of bytes is the reverse of x86 Intel
- words, which uses little-endian format.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- HIGH-VALUES signify a sort order so that the value is the highest possible
- (sorts last). HIGH-VALUES for a character key would be 0xFF for each byte, or
- the 256th byte of the collate-sequence table if an NLS sort (which is 0xFF
- also). For 16-bit signed integer values, 0x7FFF is the highest. And so on...